diff --git a/modules/addon_terminal/terminal.lua b/modules/addon_terminal/terminal.lua index c5fd92dc..5fdb07c0 100644 --- a/modules/addon_terminal/terminal.lua +++ b/modules/addon_terminal/terminal.lua @@ -173,6 +173,11 @@ end function Terminal.executeCommand(command) if command == nil or #command == 0 then return end + + logLocked = true + Logger.log(LogInfo, '>> ' .. command) + logLocked = false + -- detect and convert commands with simple syntax local realCommand if commandEnv[command] then diff --git a/modules/client_tibiafiles b/modules/client_tibiafiles index dd648e14..9beb17da 160000 --- a/modules/client_tibiafiles +++ b/modules/client_tibiafiles @@ -1 +1 @@ -Subproject commit dd648e1431171bffe091b748744395780df7eba1 +Subproject commit 9beb17daaeb170c127c39c5a5e4feb9d95ebed92 diff --git a/modules/core_lib/const.lua b/modules/core_lib/const.lua index fb398199..88f0c3fb 100644 --- a/modules/core_lib/const.lua +++ b/modules/core_lib/const.lua @@ -298,3 +298,18 @@ KeyCodeDescs = { [KeyNumpad8] = 'Numpad8', [KeyNumpad9] = 'Numpad9' } + +SpeakSay = 1 +SpeakWhisper = 2 +SpeakYell = 3 +SpeakBroadcast = 4 +SpeakPrivate = 5 +SpeakPrivateRed = 6 +SpeakPrivatePlayerToNpc = 7 +SpeakPrivateNpcToPlayer = 8 +SpeakChannelYellow = 9 +SpeakChannelWhite = 10 +SpeakChannelRed = 11 +SpeakChannelOrange = 12 +SpeakMonsterSay = 13 +SpeakMonsterYell = 14 diff --git a/modules/core_lib/settings.lua b/modules/core_lib/settings.lua index cd2a51b2..60e76c82 100644 --- a/modules/core_lib/settings.lua +++ b/modules/core_lib/settings.lua @@ -1,5 +1,13 @@ Settings = {} +Settings.exists = g_configs.exists +Settings.setNode = g_configs.setNode +Settings.addNode = g_configs.addNode +Settings.getNode = g_configs.getNode +Settings.remove = g_configs.remove +Settings.setList = g_configs.setList +Settings.getList = g_configs.getList + local function convertSettingValue(value) if type(value) == 'table' then if value.x and value.width then @@ -20,21 +28,10 @@ local function convertSettingValue(value) end end -function Settings.exists(key) - return g_configs.exists(key) -end - -function Settings.remove(key) - g_configs.remove(key) -end - function Settings.set(key, value) g_configs.set(key, convertSettingValue(value)) end -function Settings.setList(key, list) - g_configs.setList(key, list) -end function Settings.setDefault(key, value) if Settings.exists(key) then return false end @@ -49,10 +46,6 @@ function Settings.get(key, default) return g_configs.get(key) end -function Settings.getList(key) - return g_configs.getList(key) -end - function Settings.getString(key, default) return Settings.get(key, default) end diff --git a/modules/core_widgets/tooltip/tooltip.lua b/modules/core_widgets/tooltip/tooltip.lua index b890e925..5637a3c0 100644 --- a/modules/core_widgets/tooltip/tooltip.lua +++ b/modules/core_widgets/tooltip/tooltip.lua @@ -13,7 +13,7 @@ local function moveToolTip(tooltip) else pos.x = pos.x + 10 end - tooltip:setPos(pos) + tooltip:setPosition(pos) end -- public functions diff --git a/modules/core_widgets/uiitem.lua b/modules/core_widgets/uiitem.lua index ebb41d41..f6fd2665 100644 --- a/modules/core_widgets/uiitem.lua +++ b/modules/core_widgets/uiitem.lua @@ -1,6 +1,8 @@ function UIItem:onDragEnter(mousePos) + if self:isVirtual() then return false end + local item = self:getItem() - if not item then return false end + if not item then return true end self:setBorderWidth(1) @@ -11,28 +13,32 @@ function UIItem:onDragEnter(mousePos) end function UIItem:onDragLeave(widget, mousePos) + if self:isVirtual() then return false end + if not self.parsed then self.currentDragThing = nil end - + restoreCursor() self:setBorderWidth(0) return true end function UIItem:onDrop(widget, mousePos) - if not widget or not widget.currentDragThing then return false end + if self:isVirtual() then return false end + + if not widget or not widget.currentDragThing then return true end local pos = self.position - local data = widget.currentDragThing:getData() - if widget.currentDragThing:isStackable() and data > 1 then + local count = widget.currentDragThing:getCount() + if widget.currentDragThing:isStackable() and count > 1 then widget.parsed = true local moveWindow = displayUI('/game/movewindow.otui') local spinbox = moveWindow:getChildById('spinbox') - spinbox:setMaximum(data) + spinbox:setMaximum(count) spinbox:setMinimum(1) - spinbox:setCurrentIndex(data) - + spinbox:setCurrentIndex(count) + local okButton = moveWindow:getChildById('buttonOk') okButton.onClick = function() Game.move(widget.currentDragThing, pos, spinbox:getCurrentIndex()) okButton:getParent():destroy() widget.currentDragThing = nil end moveWindow.onEnter = okButton.onClick @@ -45,6 +51,8 @@ function UIItem:onDrop(widget, mousePos) end function UIItem:onHoverChange(hovered) + if self:isVirtual() then return end + if g_ui.getDraggingWidget() and self ~= g_ui.getDraggingWidget() then if hovered then self:setBorderWidth(1) @@ -55,6 +63,8 @@ function UIItem:onHoverChange(hovered) end function UIItem:onMouseRelease(mousePosition, mouseButton) + if self:isVirtual() then return false end + local item = self:getItem() if not item or not self:containsPoint(mousePosition) then return false end return Game.processMouseAction(mousePosition, mouseButton, nil, item, item, nil, item) diff --git a/modules/core_widgets/uipopupmenu.lua b/modules/core_widgets/uipopupmenu.lua index 8ba2ae41..3b8d9e07 100644 --- a/modules/core_widgets/uipopupmenu.lua +++ b/modules/core_widgets/uipopupmenu.lua @@ -65,7 +65,7 @@ function UIPopupMenu:onKeyPress(keyCode, keyboardModifiers, wouldFilter) return false end - -- close all menus when the window is resized +-- close all menus when the window is resized local function onRootGeometryUpdate() for i,menu in ipairs(displayedMenuList) do menu:destroy() diff --git a/modules/game/game.lua b/modules/game/game.lua index 6574e10b..ca71dee7 100644 --- a/modules/game/game.lua +++ b/modules/game/game.lua @@ -21,12 +21,12 @@ local function onUseWithMouseRelease(self, mousePosition, mouseButton) if mouseButton == MouseLeftButton then local clickedWidget = Game.gameUi:recursiveGetChildByPos(mousePosition) if clickedWidget then - if clickedWidget.getTile then + if clickedWidget:getClassName() == 'UIMap' then local tile = clickedWidget:getTile(mousePosition) if tile then Game.useWith(Game.selectedThing, tile:getTopMultiUseThing()) end - elseif clickedWidget.getItem then + elseif clickedWidget:getClassName() == 'UIItem' and not clickedWidget:isVirtual() then Game.useWith(Game.selectedThing, clickedWidget:getItem()) end end diff --git a/modules/game/map.lua b/modules/game/map.lua index 33b5cf24..d676e350 100644 --- a/modules/game/map.lua +++ b/modules/game/map.lua @@ -15,7 +15,7 @@ function UIMap:onDragLeave(widget, mousePos) if not self.parsed then self.currentDragThing = nil end - + restoreCursor() return true end @@ -25,21 +25,21 @@ function UIMap:onDrop(widget, mousePos) local tile = self:getTile(mousePos) if not tile then return false end - - local data = widget.currentDragThing:getData() + + local count = widget.currentDragThing:getCount() if widget.currentDragThing:isStackable() and data > 1 then widget.parsed = true local moveWindow = displayUI('/game/movewindow.otui') local spinbox = moveWindow:getChildById('spinbox') - spinbox:setMaximum(data) + spinbox:setMaximum(count) spinbox:setMinimum(1) - spinbox:setCurrentIndex(data) - + spinbox:setCurrentIndex(count) + local okButton = moveWindow:getChildById('buttonOk') - okButton.onClick = function() Game.move(widget.currentDragThing, tile:getPos(), spinbox:getCurrentIndex()) okButton:getParent():destroy() widget.currentDragThing = nil end + okButton.onClick = function() Game.move(widget.currentDragThing, tile:getPosition(), spinbox:getCurrentIndex()) okButton:getParent():destroy() widget.currentDragThing = nil end moveWindow.onEnter = okButton.onClick else - Game.move(widget.currentDragThing, tile:getPos(), 1) + Game.move(widget.currentDragThing, tile:getPosition(), 1) end return true diff --git a/modules/game/thing.lua b/modules/game/thing.lua index e96c7b1b..8cea5aa6 100644 --- a/modules/game/thing.lua +++ b/modules/game/thing.lua @@ -1,11 +1,11 @@ function Thing:isInsideContainer() - local pos = self:getPos() + local pos = self:getPosition() return (pos and pos.x == 65535 and pos.y >= 64) end function Thing:getContainerId() - local pos = self:getPos() + local pos = self:getPosition() if not pos then return 0 end return pos.y - 64 end diff --git a/modules/game_containers/containers.lua b/modules/game_containers/containers.lua index 59698924..55fcabd4 100644 --- a/modules/game_containers/containers.lua +++ b/modules/game_containers/containers.lua @@ -46,7 +46,7 @@ function Containers.onContainerOpen(containerId, itemId, name, capacity, hasPare if i <= #items then local item = items[i] - item:setPos(itemWidget.position) + item:setPosition(itemWidget.position) itemWidget:setItem(item) end end @@ -76,7 +76,7 @@ function Containers.onContainerAddItem(containerId, item) local swapItem = itemWidget:getItem() if swapItem then - swapItem:setPos(nextItemWidget.position) + swapItem:setPosition(nextItemWidget.position) nextItemWidget:setItem(swapItem) end @@ -85,7 +85,7 @@ function Containers.onContainerAddItem(containerId, item) local itemWidget = container:getChildByIndex(1) if not itemWidget then return end - item:setPos(itemWidget.position) + item:setPosition(itemWidget.position) itemWidget:setItem(item) container.itemCount = container.itemCount + 1 @@ -98,7 +98,7 @@ function Containers.onContainerUpdateItem(containerId, slot, item) local itemWidget = container:getChildByIndex(slot + 1) if not itemWidget then return end itemWidget:setItem(item) - item:setPos(itemWidget.position) + item:setPosition(itemWidget.position) end function Containers.onContainerRemoveItem(containerId, slot) @@ -117,9 +117,9 @@ function Containers.onContainerRemoveItem(containerId, slot) if not nextItemWidget then return end local item = nextItemWidget:getItem() - local pos = item:getPos() + local pos = item:getPosition() pos.z = pos.z - 1 - item:setPos(pos) + item:setPosition(pos) itemWidget:setItem(item) nextItemWidget:setItem(nil) diff --git a/modules/game_shaders/item.frag b/modules/game_shaders/item.frag new file mode 100644 index 00000000..abc75765 --- /dev/null +++ b/modules/game_shaders/item.frag @@ -0,0 +1,10 @@ +uniform float opacity; // painter opacity +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 + +void main() +{ + gl_FragColor = texture2D(texture, textureCoords) * opacity; +} diff --git a/modules/game_shaders/outfit.frag b/modules/game_shaders/outfit.frag index 214d9089..edff06fb 100644 --- a/modules/game_shaders/outfit.frag +++ b/modules/game_shaders/outfit.frag @@ -11,24 +11,19 @@ uniform vec4 bodyColor; uniform vec4 legsColor; uniform vec4 feetColor; -const vec4 yellow = vec4(1.0, 1.0, 0.0, 1.0); -const vec4 red = vec4(1.0, 0.0, 0.0, 1.0); -const vec4 green = vec4(0.0, 1.0, 0.0, 1.0); -const vec4 blue = vec4(0.0, 0.0, 1.0, 1.0); - vec4 calcOutfitPixel() { vec4 pixel = texture2D(texture, textureCoords); vec4 maskColor = texture2D(maskTexture, textureCoords); vec4 outColor = vec4(1.0, 1.0, 1.0, 1.0); - if(maskColor == yellow) + if(maskColor.r > 0.1 && maskColor.g > 0.1) outColor = headColor; - else if(maskColor == red) + else if(maskColor.r > 0.1) outColor = bodyColor; - else if(maskColor == green) + else if(maskColor.g > 0.1) outColor = legsColor; - else if(maskColor == blue) + else if(maskColor.b > 0.1) outColor = feetColor; return pixel * outColor; diff --git a/src/framework/core/configmanager.cpp b/src/framework/core/configmanager.cpp index fc1ade93..924b669b 100644 --- a/src/framework/core/configmanager.cpp +++ b/src/framework/core/configmanager.cpp @@ -112,3 +112,22 @@ void ConfigManager::remove(const std::string& key) if(child) m_confsDoc->removeChild(child); } + +void ConfigManager::setNode(const std::string& key, const OTMLNodePtr& node) +{ + remove(key); + addNode(key, node); +} + +void ConfigManager::addNode(const std::string& key, const OTMLNodePtr& node) +{ + OTMLNodePtr clone = node->clone(); + node->setTag(key); + node->setUnique(true); + m_confsDoc->addChild(node); +} + +OTMLNodePtr ConfigManager::getNode(const std::string& key) +{ + return m_confsDoc->get(key); +} diff --git a/src/framework/core/configmanager.h b/src/framework/core/configmanager.h index 575836eb..0e4ee758 100644 --- a/src/framework/core/configmanager.h +++ b/src/framework/core/configmanager.h @@ -37,6 +37,11 @@ public: void setList(const std::string& key, const std::vector& list); std::string get(const std::string& key); std::vector getList(const std::string& key); + + void setNode(const std::string& key, const OTMLNodePtr& node); + void addNode(const std::string& key, const OTMLNodePtr& node); + OTMLNodePtr getNode(const std::string& key); + bool exists(const std::string& key); void remove(const std::string& key); diff --git a/src/framework/core/eventdispatcher.cpp b/src/framework/core/eventdispatcher.cpp index 92bdb989..599115f1 100644 --- a/src/framework/core/eventdispatcher.cpp +++ b/src/framework/core/eventdispatcher.cpp @@ -28,7 +28,8 @@ EventDispatcher g_dispatcher; void EventDispatcher::flush() { - poll(); + while(!m_eventList.empty()) + poll(); while(!m_scheduledEventList.empty()) m_scheduledEventList.pop(); @@ -44,7 +45,8 @@ void EventDispatcher::poll() scheduledEvent->execute(); } - while(!m_eventList.empty()) { + int maxEvents = m_eventList.size(); + for(int i=0;iexecute(); diff --git a/src/framework/core/eventdispatcher.h b/src/framework/core/eventdispatcher.h index 46022c3e..074c0034 100644 --- a/src/framework/core/eventdispatcher.h +++ b/src/framework/core/eventdispatcher.h @@ -33,7 +33,7 @@ public: Event(const SimpleCallback& callback) : m_callback(callback), m_canceled(false), m_executed(false) { } void execute() { - if(!m_canceled) { + if(!m_canceled && !m_executed) { m_callback(); m_executed = true; } diff --git a/src/framework/core/resourcemanager.cpp b/src/framework/core/resourcemanager.cpp index a0debf80..8e7db4e9 100644 --- a/src/framework/core/resourcemanager.cpp +++ b/src/framework/core/resourcemanager.cpp @@ -123,7 +123,7 @@ bool ResourceManager::saveFile(const std::string& fileName, const uchar* data, u return true; } -bool ResourceManager::saveFile(const std::string& fileName, std::istream& in) +bool ResourceManager::saveFile(const std::string& fileName, std::iostream& in) { std::streampos oldPos = in.tellg(); in.seekg(0, std::ios::end); diff --git a/src/framework/core/resourcemanager.h b/src/framework/core/resourcemanager.h index 54920e5f..d576b1cb 100644 --- a/src/framework/core/resourcemanager.h +++ b/src/framework/core/resourcemanager.h @@ -44,7 +44,7 @@ public: bool saveFile(const std::string& fileName, const uchar* data, uint size); bool saveFile(const std::string& fileName, const std::string& data); - bool saveFile(const std::string& fileName, std::istream& in); + bool saveFile(const std::string& fileName, std::iostream& in); bool deleteFile(const std::string& fileName); diff --git a/src/framework/graphics/framebuffer.cpp b/src/framework/graphics/framebuffer.cpp index 4dafd5af..8ead7420 100644 --- a/src/framework/graphics/framebuffer.cpp +++ b/src/framework/graphics/framebuffer.cpp @@ -26,6 +26,15 @@ uint FrameBuffer::boundFbo = 0; +FrameBuffer::FrameBuffer() +{ + m_clearColor = Fw::alpha; + + glGenFramebuffers(1, &m_fbo); + if(!m_fbo) + logFatal("Unable to create framebuffer object"); +} + FrameBuffer::FrameBuffer(const Size& size) { m_clearColor = Fw::alpha; @@ -44,6 +53,9 @@ FrameBuffer::~FrameBuffer() void FrameBuffer::resize(const Size& size) { + if(!size.isValid()) + return; + if(m_texture && m_texture->getSize() == size) return; @@ -83,6 +95,16 @@ void FrameBuffer::release() g_graphics.setViewportSize(m_oldViewportSize); } +void FrameBuffer::generateMipmaps() +{ + m_texture->generateMipmaps(); +} + +void FrameBuffer::draw(const Rect& dest, const Rect& src) +{ + g_painter.drawTexturedRect(dest, m_texture, src); +} + void FrameBuffer::draw(const Rect& dest) { g_painter.drawTexturedRect(dest, m_texture); diff --git a/src/framework/graphics/framebuffer.h b/src/framework/graphics/framebuffer.h index bd81d6a4..321350b3 100644 --- a/src/framework/graphics/framebuffer.h +++ b/src/framework/graphics/framebuffer.h @@ -24,21 +24,26 @@ #define FRAMEBUFFER_H #include "declarations.h" +#include "texture.h" class FrameBuffer { public: + FrameBuffer(); FrameBuffer(const Size& size); virtual ~FrameBuffer(); void resize(const Size& size); void bind(bool clear = true); void release(); + void generateMipmaps(); void draw(const Rect& dest); + void draw(const Rect& dest, const Rect& src); void setClearColor(const Color& color) { m_clearColor = color; } TexturePtr getTexture() { return m_texture; } + const Size& getSize() { return m_texture->getSize(); } private: void internalBind(); diff --git a/src/framework/graphics/graphics.cpp b/src/framework/graphics/graphics.cpp index d4e26986..35951227 100644 --- a/src/framework/graphics/graphics.cpp +++ b/src/framework/graphics/graphics.cpp @@ -106,3 +106,10 @@ void Graphics::setViewportSize(const Size& size) m_viewportSize = size; } +int Graphics::getMaxTextureSize() +{ + static GLint maxTexSize = -1; + if(maxTexSize == -1) + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize); + return maxTexSize; +} diff --git a/src/framework/graphics/graphics.h b/src/framework/graphics/graphics.h index 4e5adf8c..b811bb7e 100644 --- a/src/framework/graphics/graphics.h +++ b/src/framework/graphics/graphics.h @@ -40,6 +40,7 @@ public: void setViewportSize(const Size& size); + int getMaxTextureSize(); const Size& getViewportSize() { return m_viewportSize; } TexturePtr getEmptyTexture() { return m_emptyTexture; } diff --git a/src/framework/graphics/painter.cpp b/src/framework/graphics/painter.cpp index 176ae75e..12638d55 100644 --- a/src/framework/graphics/painter.cpp +++ b/src/framework/graphics/painter.cpp @@ -60,8 +60,8 @@ void Painter::drawProgram(const PainterShaderProgramPtr& program, CoordsBuffer& return; program->setProjectionMatrix(m_projectionMatrix); - program->setOpacity(m_currentOpacity); - program->setColor(m_currentColor); + program->setOpacity(m_opacity); + program->setColor(m_color); program->draw(coordsBuffer, drawMode); } @@ -141,4 +141,32 @@ void Painter::setCompositionMode(Painter::CompositionMode compositionMode) glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA); break; } + m_compostionMode = compositionMode; +} + +void Painter::saveAndResetState() +{ + m_oldCustomProgram = m_customProgram; + m_oldProjectionMatrix = m_projectionMatrix; + m_oldColor = m_color; + m_oldOpacity = m_opacity; + m_oldCompostionMode = m_compostionMode; + + releaseCustomProgram(); + setColor(Fw::white); + setOpacity(1); + setCompositionMode(CompositionMode_Normal); +} + +void Painter::restoreSavedState() +{ + setCustomProgram(m_oldCustomProgram); + setColor(m_oldColor); + setOpacity(m_oldOpacity); + setCompositionMode(m_oldCompostionMode); + + m_oldCustomProgram = nullptr; + m_oldColor = Fw::white; + m_oldOpacity = 1; + m_oldCompostionMode = CompositionMode_Normal; } diff --git a/src/framework/graphics/painter.h b/src/framework/graphics/painter.h index 7a30632d..4693e353 100644 --- a/src/framework/graphics/painter.h +++ b/src/framework/graphics/painter.h @@ -50,11 +50,11 @@ public: void drawFilledRect(const Rect& dest); void drawBoundingRect(const Rect& dest, int innerLineWidth = 1); - void setColor(const Color& color) { m_currentColor = color; } - Color getColor() { return m_currentColor; } + void setColor(const Color& color) { m_color = color; } + Color getColor() { return m_color; } - void setOpacity(float opacity) { m_currentOpacity = opacity; } - float getOpacity() { return m_currentOpacity; } + void setOpacity(float opacity) { m_opacity = opacity; } + float getOpacity() { return m_opacity; } void setCustomProgram(PainterShaderProgramPtr program); void releaseCustomProgram() { m_customProgram = nullptr; } @@ -64,14 +64,24 @@ public: void setProjectionMatrix(const Matrix3& projectionMatrix) { m_projectionMatrix = projectionMatrix; } Matrix3 getProjectionMatrix() { return m_projectionMatrix; } + void saveAndResetState(); + void restoreSavedState(); + private: PainterShaderProgramPtr m_drawTexturedProgram; PainterShaderProgramPtr m_drawSolidColorProgram; PainterShaderProgramPtr m_customProgram; Matrix3 m_projectionMatrix; - Color m_currentColor; - float m_currentOpacity; + Color m_color; + float m_opacity; + CompositionMode m_compostionMode; CoordsBuffer m_coordsBuffer; + + PainterShaderProgramPtr m_oldCustomProgram; + Matrix3 m_oldProjectionMatrix; + Color m_oldColor; + float m_oldOpacity; + CompositionMode m_oldCompostionMode; }; extern Painter g_painter; diff --git a/src/framework/graphics/paintershaderprogram.cpp b/src/framework/graphics/paintershaderprogram.cpp index 9a79f8b7..9cf48bbb 100644 --- a/src/framework/graphics/paintershaderprogram.cpp +++ b/src/framework/graphics/paintershaderprogram.cpp @@ -26,6 +26,11 @@ #include "texturemanager.h" #include +PainterShaderProgram::PainterShaderProgram() +{ + m_textures.fill(std::make_tuple(-1, 0)); +} + bool PainterShaderProgram::link() { bindAttributeLocation(VERTEX_COORDS_ATTR, "vertexCoord"); @@ -63,12 +68,9 @@ void PainterShaderProgram::setOpacity(float opacity) void PainterShaderProgram::setUniformTexture(int location, const TexturePtr& texture, int index) { - if(index > 0) - glActiveTexture(GL_TEXTURE0 + index); - glBindTexture(GL_TEXTURE_2D, texture ? texture->getId() : 0); - if(index > 0) - glActiveTexture(GL_TEXTURE0); - setUniformValue(location, index); + assert(index >= 0 && index <= 1); + + m_textures[index] = std::make_tuple(location, texture ? texture->getId() : 0); } void PainterShaderProgram::setTexture(const TexturePtr& texture) @@ -112,6 +114,18 @@ void PainterShaderProgram::draw(const CoordsBuffer& coordsBuffer, DrawMode drawM mustDisableTexCoordsArray = true; } + for(int i=0;i<(int)m_textures.size();++i) { + int location = std::get<0>(m_textures[i]); + if(location == -1) + break; + int id = std::get<1>(m_textures[i]); + setUniformValue(location, i); + + glActiveTexture(GL_TEXTURE0+i); + glBindTexture(GL_TEXTURE_2D, id); + } + glActiveTexture(GL_TEXTURE0); + glDrawArrays(drawMode, 0, numVertices); if(mustDisableVertexArray) diff --git a/src/framework/graphics/paintershaderprogram.h b/src/framework/graphics/paintershaderprogram.h index a9c376c8..4f492a5a 100644 --- a/src/framework/graphics/paintershaderprogram.h +++ b/src/framework/graphics/paintershaderprogram.h @@ -45,6 +45,8 @@ public: TriangleStrip = GL_TRIANGLE_STRIP }; + PainterShaderProgram(); + bool link(); void setProjectionMatrix(const Matrix3& projectionMatrix); @@ -57,6 +59,7 @@ public: private: DrawMode m_drawMode; Timer m_startTimer; + std::array, 4> m_textures; }; #endif diff --git a/src/framework/graphics/particle.cpp b/src/framework/graphics/particle.cpp index 972dd791..91f8700a 100644 --- a/src/framework/graphics/particle.cpp +++ b/src/framework/graphics/particle.cpp @@ -29,7 +29,7 @@ Particle::Particle(const Point& pos, const Size& startSize, const Size& finalSiz m_colors = colors; m_colorsStops = colorsStops; - m_pos = PointF(pos.x, pos.y); + m_position = PointF(pos.x, pos.y); m_startSize = startSize; m_finalSize = finalSize; m_velocity = velocity; @@ -80,18 +80,18 @@ void Particle::updatePosition(double elapsedTime) PointF delta = m_velocity * elapsedTime; delta.y *= -1; // painter orientate Y axis in the inverse direction - PointF position = m_pos + delta; + PointF position = m_position + delta; - if(m_pos != position) { + if(m_position != position) { mustRedraw = true; - m_pos += delta; + m_position += delta; } // update acceleration m_velocity += m_acceleration * elapsedTime; } - m_rect.move((int)m_pos.x - m_size.width() / 2, (int)m_pos.y - m_size.height() / 2); + m_rect.move((int)m_position.x - m_size.width() / 2, (int)m_position.y - m_size.height() / 2); } void Particle::updateSize() diff --git a/src/framework/graphics/particle.h b/src/framework/graphics/particle.h index 692f4131..305cf1d9 100644 --- a/src/framework/graphics/particle.h +++ b/src/framework/graphics/particle.h @@ -36,10 +36,10 @@ public: bool hasFinished() { return m_finished; } - PointF getPos() { return m_pos; } + PointF getPosition() { return m_position; } PointF getVelocity() { return m_velocity; } - void setPos(const PointF& position) { m_pos = position; } + void setPosition(const PointF& position) { m_position = position; } void setVelocity(const PointF& velocity) { m_velocity = velocity; } private: @@ -51,7 +51,7 @@ private: std::vector m_colors; std::vector m_colorsStops; TexturePtr m_texture; - PointF m_pos; + PointF m_position; PointF m_velocity; PointF m_acceleration; Size m_size, m_startSize, m_finalSize; diff --git a/src/framework/graphics/particleaffector.cpp b/src/framework/graphics/particleaffector.cpp index 1576ca67..0596e6a2 100644 --- a/src/framework/graphics/particleaffector.cpp +++ b/src/framework/graphics/particleaffector.cpp @@ -115,7 +115,7 @@ bool AttractionAffector::load(const OTMLNodePtr& node) for(const OTMLNodePtr& childNode : node->children()) { if(childNode->tag() == "position") - m_pos = childNode->value(); + m_position = childNode->value(); else if(childNode->tag() == "acceleration") m_acceleration = childNode->value(); else if(childNode->tag() == "velocity-reduction-percent") @@ -131,8 +131,8 @@ void AttractionAffector::updateParticle(const ParticlePtr& particle, double elap if(!m_active) return; - PointF pPosition = particle->getPos(); - PointF d = PointF(m_pos.x - pPosition.x, pPosition.y - m_pos.y); + PointF pPosition = particle->getPosition(); + PointF d = PointF(m_position.x - pPosition.x, pPosition.y - m_position.y); if(d.length() == 0) return; diff --git a/src/framework/graphics/particleaffector.h b/src/framework/graphics/particleaffector.h index 3f72116f..dbfd2f2c 100644 --- a/src/framework/graphics/particleaffector.h +++ b/src/framework/graphics/particleaffector.h @@ -57,7 +57,7 @@ public: void updateParticle(const ParticlePtr& particle, double elapsedTime); private: - Point m_pos; + Point m_position; float m_acceleration, m_reduction; bool m_repelish; }; diff --git a/src/framework/graphics/particleemitter.cpp b/src/framework/graphics/particleemitter.cpp index 2b5c18fd..8111291a 100644 --- a/src/framework/graphics/particleemitter.cpp +++ b/src/framework/graphics/particleemitter.cpp @@ -31,7 +31,7 @@ ParticleEmitter::ParticleEmitter(const ParticleSystemPtr& parent) { m_parent = parent; - m_pos = Point(0, 0); + m_position = Point(0, 0); m_duration = -1; m_delay = 0; m_burstRate = 1; m_burstCount = 32; @@ -65,7 +65,7 @@ bool ParticleEmitter::load(const OTMLNodePtr& node) for(const OTMLNodePtr& childNode : node->children()) { // self related if(childNode->tag() == "position") - m_pos = childNode->value(); + m_position = childNode->value(); else if(childNode->tag() == "duration") m_duration = childNode->value(); else if(childNode->tag() == "delay") @@ -199,7 +199,7 @@ void ParticleEmitter::update(double elapsedTime) float pRadius = Fw::randomRange(m_pMinPositionRadius, m_pMaxPositionRadius); float pAngle = Fw::randomRange(m_pMinPositionAngle, m_pMaxPositionAngle); - Point pPosition = m_pos + Point(pRadius * cos(pAngle), pRadius * sin(pAngle)); + Point pPosition = m_position + Point(pRadius * cos(pAngle), pRadius * sin(pAngle)); for(int p = 0; p < m_burstCount; ++p) { diff --git a/src/framework/graphics/particleemitter.h b/src/framework/graphics/particleemitter.h index 12e88ef0..402a96e4 100644 --- a/src/framework/graphics/particleemitter.h +++ b/src/framework/graphics/particleemitter.h @@ -44,7 +44,7 @@ private: ParticleSystemWeakPtr m_parent; // self related - Point m_pos; + Point m_position; float m_duration, m_delay; double m_elapsedTime; bool m_finished, m_active; diff --git a/src/framework/graphics/texture.cpp b/src/framework/graphics/texture.cpp index 0d9cd821..8b6d48a7 100644 --- a/src/framework/graphics/texture.cpp +++ b/src/framework/graphics/texture.cpp @@ -89,39 +89,127 @@ uint Texture::internalLoadGLTexture(uchar *pixels, int channels, int width, int glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - // nearest filtering (non smooth) - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + setupFilters(); + return id; } +void Texture::generateMipmaps() +{ + bind(); + + if(!m_useMipmaps) { + m_useMipmaps = true; + setupFilters(); + } + + glGenerateMipmap(GL_TEXTURE_2D); +} + void Texture::setSmooth(bool smooth) { if(smooth == m_smooth) return; - if(smooth) { - // enable smooth texture - glBindTexture(GL_TEXTURE_2D, m_textureId); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - } else { - // nearest filtering (non smooth) - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - } - - m_smooth = true; + m_smooth = smooth; + bind(); + setupFilters(); } std::vector Texture::getPixels() { - // hack to copy pixels from opengl memory - FrameBufferPtr fb(new FrameBuffer(m_size)); std::vector pixels(m_size.area()*4, 0); +#ifdef OPENGL_ES + // hack to copy pixels from opengl memory in opengl es + // NOTE: this can be slow, but its the only way to get pixels from a texture in OpenGL ES + FrameBufferPtr fb(new FrameBuffer(m_size)); fb->bind(); + g_painter.saveAndResetState(); g_painter.drawTexturedRect(Rect(0,0,m_size), shared_from_this()); glReadPixels(0, 0, m_size.width(), m_size.height(), GL_RGBA, GL_UNSIGNED_BYTE, &pixels[0]); + g_painter.restoreSavedState(); fb->release(); +#else + // copy pixels from opengl memory + glBindTexture(GL_TEXTURE_2D, m_textureId); + glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, &pixels[0]); +#endif return pixels; } + +void Texture::generateBilinearMipmaps(std::vector inPixels) +{ + bind(); + + if(!m_useMipmaps) { + m_useMipmaps = true; + setupFilters(); + } + + Size inSize = getSize(); + Size outSize = inSize / 2; + std::vector outPixels(outSize.area()*4); + + int mipmap = 1; + while(true) { + for(int x=0;x 0) + outPixel[i] = pixelsSum[i] / usedPixels; + else + outPixel[i] = 0; + } + + outPixel[3] = pixelsSum[3]/4; + } + } + + glTexImage2D(GL_TEXTURE_2D, mipmap++, GL_RGBA, outSize.width(), outSize.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, &outPixels[0]); + + if(inSize.width() == 1 || inSize.height() == 1) + break; + + inPixels = std::move(outPixels); + inSize /= 2; + outSize /= 2; + } +} + +void Texture::setupFilters() +{ + GLint minFilter; + GLint magFilter; + if(m_smooth) { + minFilter = m_useMipmaps ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR; + magFilter = GL_LINEAR; + } else { + minFilter = m_useMipmaps ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST; + magFilter = GL_NEAREST; + } + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter); +} diff --git a/src/framework/graphics/texture.h b/src/framework/graphics/texture.h index 095e44d7..5850db73 100644 --- a/src/framework/graphics/texture.h +++ b/src/framework/graphics/texture.h @@ -32,7 +32,14 @@ public: Texture(int width, int height, int channels, uchar* pixels = NULL); virtual ~Texture(); - virtual void setSmooth(bool smooth); + void bind() { glBindTexture(GL_TEXTURE_2D, m_textureId); } + + void generateMipmaps(); + + // generate bilinear mipmaps optimized for alpha textures + void generateBilinearMipmaps(std::vector inPixels); + + void setSmooth(bool smooth); GLuint getId() { return m_textureId; } std::vector getPixels(); @@ -44,10 +51,12 @@ public: bool isEmpty() const { return m_textureId == 0; } protected: + void setupFilters(); GLuint internalLoadGLTexture(uchar* pixels, int channels, int w, int h); GLuint m_textureId; Size m_size; + Boolean m_useMipmaps; Boolean m_smooth; }; diff --git a/src/framework/luafunctions.cpp b/src/framework/luafunctions.cpp index 24397901..8068516b 100644 --- a/src/framework/luafunctions.cpp +++ b/src/framework/luafunctions.cpp @@ -154,7 +154,7 @@ void Application::registerLuaFunctions() g_lua.bindClassMemberFunction("setWidth", &UIWidget::setWidth); g_lua.bindClassMemberFunction("setHeight", &UIWidget::setHeight); g_lua.bindClassMemberFunction("setSize", &UIWidget::setSize); - g_lua.bindClassMemberFunction("setPos", &UIWidget::setPos); + g_lua.bindClassMemberFunction("setPosition", &UIWidget::setPosition); g_lua.bindClassMemberFunction("setColor", &UIWidget::setColor); g_lua.bindClassMemberFunction("setBackgroundColor", &UIWidget::setBackgroundColor); g_lua.bindClassMemberFunction("setBackgroundOffsetX", &UIWidget::setBackgroundOffsetX); @@ -192,7 +192,7 @@ void Application::registerLuaFunctions() g_lua.bindClassMemberFunction("setOpacity", &UIWidget::setOpacity); g_lua.bindClassMemberFunction("getX", &UIWidget::getX); g_lua.bindClassMemberFunction("getY", &UIWidget::getY); - g_lua.bindClassMemberFunction("getPos", &UIWidget::getPos); + g_lua.bindClassMemberFunction("getPosition", &UIWidget::getPosition); g_lua.bindClassMemberFunction("getWidth", &UIWidget::getWidth); g_lua.bindClassMemberFunction("getHeight", &UIWidget::getHeight); g_lua.bindClassMemberFunction("getSize", &UIWidget::getSize); @@ -381,6 +381,9 @@ void Application::registerLuaFunctions() g_lua.bindClassStaticFunction("g_configs", "getList", std::bind(&ConfigManager::getList, &g_configs, _1)); g_lua.bindClassStaticFunction("g_configs", "exists", std::bind(&ConfigManager::exists, &g_configs, _1)); g_lua.bindClassStaticFunction("g_configs", "remove", std::bind(&ConfigManager::remove, &g_configs, _1)); + g_lua.bindClassStaticFunction("g_configs", "setNode", std::bind(&ConfigManager::setNode, &g_configs, _1, _2)); + g_lua.bindClassStaticFunction("g_configs", "addNode", std::bind(&ConfigManager::addNode, &g_configs, _1, _2)); + g_lua.bindClassStaticFunction("g_configs", "getNode", std::bind(&ConfigManager::getNode, &g_configs, _1)); // PlatformWindow g_lua.registerStaticClass("g_window"); @@ -409,7 +412,7 @@ void Application::registerLuaFunctions() g_lua.bindClassStaticFunction("g_window", "getWidth", std::bind(&PlatformWindow::getWidth, &g_window)); g_lua.bindClassStaticFunction("g_window", "getHeight", std::bind(&PlatformWindow::getHeight, &g_window)); g_lua.bindClassStaticFunction("g_window", "getUnmaximizedPos", std::bind(&PlatformWindow::getUnmaximizedPos, &g_window)); - g_lua.bindClassStaticFunction("g_window", "getPos", std::bind(&PlatformWindow::getPos, &g_window)); + g_lua.bindClassStaticFunction("g_window", "getPosition", std::bind(&PlatformWindow::getPosition, &g_window)); g_lua.bindClassStaticFunction("g_window", "getX", std::bind(&PlatformWindow::getX, &g_window)); g_lua.bindClassStaticFunction("g_window", "getY", std::bind(&PlatformWindow::getY, &g_window)); g_lua.bindClassStaticFunction("g_window", "getMousePos", std::bind(&PlatformWindow::getMousePos, &g_window)); diff --git a/src/framework/luascript/luainterface.cpp b/src/framework/luascript/luainterface.cpp index 64ad66ac..77d57fda 100644 --- a/src/framework/luascript/luainterface.cpp +++ b/src/framework/luascript/luainterface.cpp @@ -48,7 +48,8 @@ void LuaInterface::init() // register LuaObject, the base of all other objects registerClass(); - bindClassMemberGetField("use_count", &LuaObject::getUseCount); + bindClassMemberFunction("getUseCount", &LuaObject::getUseCount); + bindClassMemberFunction("getClassName", &LuaObject::getClassName); } void LuaInterface::terminate() @@ -958,7 +959,7 @@ void LuaInterface::pushObject(const LuaObjectPtr& obj) new(newUserdata(sizeof(LuaObjectPtr))) LuaObjectPtr(obj); // set the userdata metatable - getGlobal(Fw::mkstr(obj->getLuaObjectName(), "_mt")); + getGlobal(Fw::mkstr(obj->getClassName(), "_mt")); assert(!isNil()); setMetatable(); } diff --git a/src/framework/luascript/luaobject.h b/src/framework/luascript/luaobject.h index e9f17276..a5de7922 100644 --- a/src/framework/luascript/luaobject.h +++ b/src/framework/luascript/luaobject.h @@ -61,8 +61,8 @@ public: /// @note each userdata of this object on lua counts as a reference int getUseCount(); - /// Returns the class name used in Lua - virtual std::string getLuaObjectName() const { + /// Returns the derived class name, its the same name used in Lua + virtual std::string getClassName() const { // TODO: this could be cached for more performance return Fw::demangleName(typeid(*this).name()); } diff --git a/src/framework/luascript/luavaluecasts.cpp b/src/framework/luascript/luavaluecasts.cpp index 0c5a268e..c1eeb903 100644 --- a/src/framework/luascript/luavaluecasts.cpp +++ b/src/framework/luascript/luavaluecasts.cpp @@ -215,7 +215,11 @@ bool luavalue_cast(int index, Size& size) void push_otml_subnode_luavalue(const OTMLNodePtr& node) { if(node->hasValue()) { - g_lua.pushString(node->value()); + // convert boolean types + if(node->value() == "true" || node->value() == "false") + g_lua.pushBoolean(node->value()); + else + g_lua.pushString(node->value()); } else if(node->hasChildren()) { g_lua.newTable(); bool pushedChild = false; @@ -225,6 +229,7 @@ void push_otml_subnode_luavalue(const OTMLNodePtr& node) if(!g_lua.isNil()) { if(cnode->isUnique()) { g_lua.pushString(cnode->tag()); + g_lua.insert(-2); g_lua.rawSet(); } else g_lua.rawSeti(currentIndex++); @@ -242,16 +247,18 @@ void push_otml_subnode_luavalue(const OTMLNodePtr& node) void push_luavalue(const OTMLNodePtr& node) { - g_lua.newTable(); - for(const OTMLNodePtr& cnode : node->children()) { - if(cnode->isUnique()) { + if(node) { + g_lua.newTable(); + int currentIndex = 1; + for(const OTMLNodePtr& cnode : node->children()) { push_otml_subnode_luavalue(cnode); - if(!g_lua.isNil()) { + if(cnode->isUnique()) { g_lua.setField(cnode->tag()); } else - g_lua.pop(); + g_lua.rawSeti(currentIndex++); } - } + } else + g_lua.pushNil(); } bool luavalue_cast(int index, OTMLNodePtr& node) @@ -261,15 +268,29 @@ bool luavalue_cast(int index, OTMLNodePtr& node) if(g_lua.isTable(index)) { g_lua.pushNil(); while(g_lua.next(index < 0 ? index-1 : index)) { - std::string cnodeName = g_lua.toString(-2); + std::string cnodeName; + if(!g_lua.isNumber(-2)) + cnodeName = g_lua.toString(-2); if(g_lua.isTable()) { OTMLNodePtr cnode; - if(luavalue_cast(-1, node)) { - cnode->setTag(cnodeName); + if(luavalue_cast(-1, cnode)) { + if(cnodeName.empty()) + node->setUnique(false); + else + cnode->setTag(cnodeName); node->addChild(cnode); } - } else - node->writeAt(cnodeName, g_lua.toString()); + } else { + std::string value; + if(g_lua.isBoolean()) + value = Fw::unsafeCast(g_lua.toBoolean()); + else + value = g_lua.toString(); + if(cnodeName.empty()) + node->writeIn(value); + else + node->writeAt(cnodeName, value); + } g_lua.pop(); } return true; diff --git a/src/framework/math/point.h b/src/framework/math/point.h index c17bd598..adcc1747 100644 --- a/src/framework/math/point.h +++ b/src/framework/math/point.h @@ -56,10 +56,10 @@ public: TPoint& operator+=(T other) { x+=other; y+=other; return *this; } TPoint operator-(T other) const { return TPoint(x - other, y - other); } TPoint& operator-=(T other) { x-=other; y-=other; return *this; } - TPoint operator*(const T v) const { return TPoint(x*v, y*v); } - TPoint& operator*=(const T v) { x*=v; y*=v; return *this; } - TPoint operator/(const T v) const { return TPoint(x/v, y/v); } - TPoint& operator/=(const T v) { x/=v; y/=v; return *this; } + TPoint operator*(float v) const { return TPoint(x*v, y*v); } + TPoint& operator*=(float v) { x*=v; y*=v; return *this; } + TPoint operator/(float v) const { return TPoint(x/v, y/v); } + TPoint& operator/=(float v) { x/=v; y/=v; return *this; } bool operator<=(const TPoint&other) const { return x<=other.x && y<=other.y; } bool operator>=(const TPoint&other) const { return x>=other.x && y>=other.y; } diff --git a/src/framework/math/size.h b/src/framework/math/size.h index a89085b2..e286697c 100644 --- a/src/framework/math/size.h +++ b/src/framework/math/size.h @@ -52,15 +52,19 @@ public: TSize& operator+=(const TSize& other) { wd+=other.wd; ht+=other.ht; return *this; } TSize operator-(const TSize& other) const { return TSize(wd - other.wd, ht - other.ht); } TSize& operator-=(const TSize& other) { wd-=other.wd; ht-=other.ht; return *this; } + TSize operator*(const TSize& other) const { return TSize((T)other.wd*wd, (T)ht*other.ht); } + TSize& operator*=(const TSize& other) { wd=(T)other.wd*wd; ht=(T)ht*other.ht; return *this; } + TSize operator/(const TSize& other) const { return TSize((T)wd/other.wd, (T)ht/other.ht); } + TSize& operator/=(const TSize& other) { (T)wd/=other.wd; (T)ht/=other.ht; return *this; } TSize operator*(const float v) const { return TSize((T)v*wd, (T)ht*v); } TSize& operator*=(const float v) { wd=(T)v*wd; ht=(T)ht*v; return *this; } TSize operator/(const float v) const { return TSize((T)wd/v, (T)ht/v); } - TSize& operator/=(const float v) { (T)wd/=v; (T)ht/=v; return *this; } + TSize& operator/=(const float v) { wd/=v; ht/=v; return *this; } - bool operator<=(const TSize&other) const { return wd<=other.wd || ht<=other.ht; } - bool operator>=(const TSize&other) const { return wd>=other.wd || ht>=other.ht; } - bool operator<(const TSize&other) const { return wd(const TSize&other) const { return wd>other.wd || ht>other.ht; } + bool operator<=(const TSize&other) const { return wd<=other.wd && ht<=other.ht; } + bool operator>=(const TSize&other) const { return wd>=other.wd && ht>=other.ht; } + bool operator<(const TSize&other) const { return wd(const TSize&other) const { return wd>other.wd && ht>other.ht; } TSize& operator=(const TSize& other) { wd = other.wd; ht = other.ht; return *this; } bool operator==(const TSize& other) const { return other.wd==wd && other.ht==ht; } diff --git a/src/framework/net/connection.cpp b/src/framework/net/connection.cpp index efa99e52..3415746f 100644 --- a/src/framework/net/connection.cpp +++ b/src/framework/net/connection.cpp @@ -35,6 +35,7 @@ Connection::Connection() : { m_connected = false; m_connecting = false; + m_sendBufferSize = 0; } Connection::~Connection() @@ -57,6 +58,7 @@ void Connection::connect(const std::string& host, uint16 port, const SimpleCallb { m_connected = false; m_connecting = true; + m_error.clear(); m_connectCallback = connectCallback; asio::ip::tcp::resolver::query query(host, Fw::unsafeCast(port)); @@ -85,6 +87,10 @@ void Connection::close() if(!m_connected && !m_connecting) return; + // flush send data before disconnecting on clean connections + if(m_connected && !m_error && m_sendBufferSize > 0 && m_sendEvent) + m_sendEvent->execute(); + m_connecting = false; m_connected = false; m_connectCallback = nullptr; @@ -104,17 +110,34 @@ void Connection::close() void Connection::write(uint8* buffer, uint16 size) { - m_writeTimer.cancel(); - if(!m_connected) return; - asio::async_write(m_socket, - asio::buffer(buffer, size), - std::bind(&Connection::onWrite, shared_from_this(), _1, _2)); + // send old buffer if we can't add more data + if(m_sendBufferSize + size >= SEND_BUFFER_SIZE && m_sendEvent) + m_sendEvent->execute(); + + // we can't send the data right away, otherwise we could create tcp congestion + memcpy(m_sendBuffer + m_sendBufferSize, buffer, size); + m_sendBufferSize += size; - m_writeTimer.expires_from_now(boost::posix_time::seconds(WRITE_TIMEOUT)); - m_writeTimer.async_wait(std::bind(&Connection::onTimeout, shared_from_this(), _1)); + if(!m_sendEvent || m_sendEvent->isExecuted() || m_sendEvent->isCanceled()) { + auto weakSelf = ConnectionWeakPtr(shared_from_this()); + m_sendEvent = g_dispatcher.scheduleEvent([=] { + if(!weakSelf.lock()) + return; + //m_writeTimer.cancel(); + + asio::async_write(m_socket, + asio::buffer(m_sendBuffer, m_sendBufferSize), + std::bind(&Connection::onWrite, shared_from_this(), _1, _2)); + + m_writeTimer.expires_from_now(boost::posix_time::seconds(WRITE_TIMEOUT)); + m_writeTimer.async_wait(std::bind(&Connection::onTimeout, shared_from_this(), _1)); + + m_sendBufferSize = 0; + }, SEND_INTERVAL); + } } void Connection::read(uint16 bytes, const RecvCallback& callback) @@ -157,7 +180,7 @@ void Connection::onConnect(const boost::system::error_code& error) if(!error) { m_connected = true; - // disable nagle's algorithm + // disable nagle's algorithm, this make the game play smoother boost::asio::ip::tcp::no_delay option(true); m_socket.set_option(option); @@ -203,6 +226,7 @@ void Connection::onTimeout(const boost::system::error_code& error) void Connection::handleError(const boost::system::error_code& error) { if(error != asio::error::operation_aborted) { + m_error = error; if(m_errorCallback) m_errorCallback(error); if(m_connected || m_connecting) diff --git a/src/framework/net/connection.h b/src/framework/net/connection.h index 0ee2975f..f78a7e07 100644 --- a/src/framework/net/connection.h +++ b/src/framework/net/connection.h @@ -25,6 +25,8 @@ #include "declarations.h" #include +#include +#include class Connection : public std::enable_shared_from_this, boost::noncopyable { @@ -34,6 +36,8 @@ class Connection : public std::enable_shared_from_this, boost::nonco enum { READ_TIMEOUT = 30, WRITE_TIMEOUT = 30, + SEND_INTERVAL = 10, + SEND_BUFFER_SIZE = 65536, RECV_BUFFER_SIZE = 65536 }; @@ -53,6 +57,7 @@ public: void setErrorCallback(const ErrorCallback& errorCallback) { m_errorCallback = errorCallback; } + boost::system::error_code getError() const { return m_error; } bool isConnecting() const { return m_connecting; } bool isConnected() const { return m_connected; } @@ -72,9 +77,14 @@ protected: asio::ip::tcp::resolver m_resolver; asio::ip::tcp::socket m_socket; + uint8 m_sendBuffer[SEND_BUFFER_SIZE]; uint8 m_recvBuffer[RECV_BUFFER_SIZE]; bool m_connected; bool m_connecting; + boost::system::error_code m_error; + int m_sendBufferSize; + Timer m_sendTimer; + ScheduledEventPtr m_sendEvent; friend class Server; }; diff --git a/src/framework/otml/otmlnode.h b/src/framework/otml/otmlnode.h index 1b1e6e74..3897d293 100644 --- a/src/framework/otml/otmlnode.h +++ b/src/framework/otml/otmlnode.h @@ -147,6 +147,7 @@ void OTMLNode::write(const T& v) { template void OTMLNode::writeAt(const std::string& childTag, const T& v) { OTMLNodePtr child = OTMLNode::create(childTag); + child->setUnique(true); child->write(v); addChild(child); } diff --git a/src/framework/platform/platformwindow.cpp b/src/framework/platform/platformwindow.cpp index 05c2cde8..af3c4bed 100644 --- a/src/framework/platform/platformwindow.cpp +++ b/src/framework/platform/platformwindow.cpp @@ -38,7 +38,7 @@ PlatformWindow& g_window = window; void PlatformWindow::updateUnmaximizedCoords() { if(!isMaximized() && !isFullscreen()) { - m_unmaximizedPos = m_pos; + m_unmaximizedPos = m_position; m_unmaximizedSize = m_size; } } diff --git a/src/framework/platform/platformwindow.h b/src/framework/platform/platformwindow.h index dd3d92f0..21b32fdb 100644 --- a/src/framework/platform/platformwindow.h +++ b/src/framework/platform/platformwindow.h @@ -73,9 +73,9 @@ public: int getWidth() { return m_size.width(); } int getHeight() { return m_size.height(); } Point getUnmaximizedPos() { return m_unmaximizedPos; } - Point getPos() { return m_pos; } - int getX() { return m_pos.x; } - int getY() { return m_pos.y; } + Point getPosition() { return m_position; } + int getX() { return m_position.x; } + int getY() { return m_position.y; } Point getMousePos() { return m_inputEvent.mousePos; } int getKeyboardModifiers() { return m_inputEvent.keyboardModifiers; } bool isKeyPressed(Fw::Key keyCode) { return m_keysState[keyCode]; } @@ -104,7 +104,7 @@ protected: Timer m_keyPressTimer; Size m_size; - Point m_pos; + Point m_position; Size m_unmaximizedSize; Point m_unmaximizedPos; InputEvent m_inputEvent; diff --git a/src/framework/platform/win32window.cpp b/src/framework/platform/win32window.cpp index ab866e22..4e160abd 100644 --- a/src/framework/platform/win32window.cpp +++ b/src/framework/platform/win32window.cpp @@ -278,7 +278,7 @@ void WIN32Window::internalCreateWindow() DWORD dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; DWORD dwStyle = WS_OVERLAPPEDWINDOW; - RECT windowRect = {m_pos.x, m_pos.y, m_pos.x + m_size.width(), m_pos.y + m_size.height()}; + RECT windowRect = {m_position.x, m_position.y, m_position.x + m_size.width(), m_position.y + m_size.height()}; AdjustWindowRectEx(&windowRect, dwStyle, FALSE, dwExStyle); updateUnmaximizedCoords(); @@ -361,14 +361,14 @@ void *WIN32Window::getExtensionProcAddress(const char *ext) void WIN32Window::move(const Point& pos) { - RECT windowRect = {pos.x, pos.y, m_pos.x + m_size.width(), m_pos.y + m_size.height()}; + RECT windowRect = {pos.x, pos.y, m_position.x + m_size.width(), m_position.y + m_size.height()}; AdjustWindowRectEx(&windowRect, WS_OVERLAPPEDWINDOW, FALSE, WS_EX_APPWINDOW | WS_EX_WINDOWEDGE); MoveWindow(m_window, windowRect.left, windowRect.top, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, TRUE); } void WIN32Window::resize(const Size& size) { - RECT windowRect = {m_pos.x, m_pos.y, m_pos.x + size.width(), m_pos.y + size.height()}; + RECT windowRect = {m_position.x, m_position.y, m_position.x + size.width(), m_position.y + size.height()}; AdjustWindowRectEx(&windowRect, WS_OVERLAPPEDWINDOW, FALSE, WS_EX_APPWINDOW | WS_EX_WINDOWEDGE); MoveWindow(m_window, windowRect.left, windowRect.top, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, TRUE); } @@ -503,8 +503,8 @@ LRESULT WIN32Window::windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar break; } case WM_MOVE: { - m_pos.x = LOWORD(lParam); - m_pos.y = HIWORD(lParam); + m_position.x = LOWORD(lParam); + m_position.y = HIWORD(lParam); break; } case WM_SIZE: { diff --git a/src/framework/platform/x11window.cpp b/src/framework/platform/x11window.cpp index aa274957..24059ea9 100644 --- a/src/framework/platform/x11window.cpp +++ b/src/framework/platform/x11window.cpp @@ -284,7 +284,7 @@ void X11Window::internalCreateWindow() updateUnmaximizedCoords(); m_window = XCreateWindow(m_display, m_rootWindow, - m_pos.x, m_pos.y, m_size.width(), m_size.height(), + m_position.x, m_position.y, m_size.width(), m_size.height(), 0, depth, InputOutput, @@ -300,7 +300,7 @@ void X11Window::internalCreateWindow() XSetWMHints(m_display, m_window, &hints); // ensure window position - XMoveWindow(m_display, m_window, m_pos.x, m_pos.y); + XMoveWindow(m_display, m_window, m_position.x, m_position.y); // handle wm_delete events m_wmDelete = XInternAtom(m_display, "WM_DELETE_WINDOW", True); @@ -580,7 +580,7 @@ void X11Window::poll() } // updates window pos - m_pos = newPos; + m_position = newPos; updateUnmaximizedCoords(); break; } diff --git a/src/framework/ui/uiwidget.cpp b/src/framework/ui/uiwidget.cpp index 8586e978..660bbf44 100644 --- a/src/framework/ui/uiwidget.cpp +++ b/src/framework/ui/uiwidget.cpp @@ -90,7 +90,7 @@ void UIWidget::drawChildren() g_painter.setColor(Fw::green); g_painter.drawBoundingRect(child->getRect()); } - //g_fonts.getDefaultFont()->renderText(child->getId(), child->getPos() + Point(2, 0), Fw::red); + //g_fonts.getDefaultFont()->renderText(child->getId(), child->getPosition() + Point(2, 0), Fw::red); g_painter.setOpacity(oldOpacity); } diff --git a/src/framework/ui/uiwidget.h b/src/framework/ui/uiwidget.h index 06ffd3d9..163a801c 100644 --- a/src/framework/ui/uiwidget.h +++ b/src/framework/ui/uiwidget.h @@ -189,7 +189,7 @@ protected: // function shortcuts public: - void resize(int width, int height) { setRect(Rect(getPos(), Size(width, height))); } + void resize(int width, int height) { setRect(Rect(getPosition(), Size(width, height))); } void move(int x, int y) { setRect(Rect(x, y, getSize())); } void hide() { setVisible(false); } void show() { setVisible(true); } @@ -262,7 +262,7 @@ public: void setWidth(int width) { resize(width, getHeight()); } void setHeight(int height) { resize(getWidth(), height); } void setSize(const Size& size) { resize(size.width(), size.height()); } - void setPos(const Point& pos) { move(pos.x, pos.y); } + void setPosition(const Point& pos) { move(pos.x, pos.y); } void setColor(const Color& color) { m_color = color; } void setBackgroundColor(const Color& color) { m_backgroundColor = color; } void setBackgroundOffsetX(int x) { m_backgroundRect.setX(x); } @@ -309,7 +309,7 @@ public: int getX() { return m_rect.x(); } int getY() { return m_rect.y(); } - Point getPos() { return m_rect.topLeft(); } + Point getPosition() { return m_rect.topLeft(); } int getWidth() { return m_rect.width(); } int getHeight() { return m_rect.height(); } Size getSize() { return m_rect.size(); } diff --git a/src/framework/ui/uiwidgetbasestyle.cpp b/src/framework/ui/uiwidgetbasestyle.cpp index 11bf5aba..65a00841 100644 --- a/src/framework/ui/uiwidgetbasestyle.cpp +++ b/src/framework/ui/uiwidgetbasestyle.cpp @@ -55,7 +55,7 @@ void UIWidget::parseBaseStyle(const OTMLNodePtr& styleNode) else if(node->tag() == "y") setY(node->value()); else if(node->tag() == "pos") - setPos(node->value()); + setPosition(node->value()); else if(node->tag() == "width") setWidth(node->value()); else if(node->tag() == "height") diff --git a/src/main.cpp b/src/main.cpp index cce17cb0..3dd67b25 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -31,5 +31,3 @@ int main(int argc, const char* argv[]) app.terminate(); return 0; } - -// for freenode: fk39HHDJJF \ No newline at end of file diff --git a/src/otclient/CMakeLists.txt b/src/otclient/CMakeLists.txt index eb653cc3..2c3ed0b5 100644 --- a/src/otclient/CMakeLists.txt +++ b/src/otclient/CMakeLists.txt @@ -27,6 +27,7 @@ SET(otclient_SOURCES ${otclient_SOURCES} # otclient core ${CMAKE_CURRENT_LIST_DIR}/core/game.cpp ${CMAKE_CURRENT_LIST_DIR}/core/map.cpp + ${CMAKE_CURRENT_LIST_DIR}/core/mapview.cpp ${CMAKE_CURRENT_LIST_DIR}/core/thingstype.cpp ${CMAKE_CURRENT_LIST_DIR}/core/spritemanager.cpp ${CMAKE_CURRENT_LIST_DIR}/core/item.cpp diff --git a/src/otclient/const.h b/src/otclient/const.h index 632355ae..775523d0 100644 --- a/src/otclient/const.h +++ b/src/otclient/const.h @@ -31,6 +31,51 @@ namespace Otc static const char* AppCompactName = "otclient"; static const char* AppVersion = "0.4.0"; + enum { + TILE_PIXELS = 32, + MAX_ELEVATION = 24, + + SEA_FLOOR = 7, + MAX_Z = 15, + UNDERGROUND_FLOOR = SEA_FLOOR+1, + VISIBLE_X_TILES = 15, + VISIBLE_Y_TILES = 11, + AWARE_UNDEGROUND_FLOOR_RANGE = 2, + AWARE_X_TILES = VISIBLE_X_TILES + 3, + AWARE_Y_TILES = VISIBLE_Y_TILES + 3, + AWARE_X_LEFT_TILES = AWARE_X_TILES/2 - 1, + AWARE_X_RIGHT_TILES = AWARE_X_TILES/2, + AWARE_Y_TOP_TILES = AWARE_Y_TILES/2 - 1, + AWARE_Y_BOTTOM_TILES = AWARE_Y_TILES/2, + + EFFECT_TICKS_PER_FRAME = 75, + INVISIBLE_TICKS_PER_FRAME = 500, + ITEM_TICKS_PER_FRAME = 500, + ANIMATED_TEXT_DURATION = 1000, + STATIC_DURATION_PER_CHARACTER = 75, + MIN_STATIC_TEXT_DURATION = 3000, + MAX_STATIC_TEXT_WIDTH = 200, + }; + + enum DrawFlags { + DrawGround = 1, + DrawGroundBorders = 2, + DrawOnBottom = 4, + DrawOnTop = 8, + DrawItems = 16, + DrawCreatures = 32, + DrawEffects = 64, + DrawMissiles = 128, + DrawCreaturesInformation = 256, + DrawStaticTexts = 512, + DrawAnimatedTexts = 1024, + DrawAnimations = 2048, + DrawWalls = DrawOnBottom | DrawOnTop, + DrawEverything = DrawGround | DrawGroundBorders | DrawWalls | DrawItems | + DrawCreatures | DrawEffects | DrawMissiles | + DrawCreaturesInformation | DrawStaticTexts | DrawAnimatedTexts | DrawAnimations + }; + enum DatOpts { DatGround = 0, DatGroundClip, @@ -219,6 +264,23 @@ namespace Otc IconPzBlock = 8192, IconPz = 16384 }; + + enum SpeakType { + SpeakSay = 1, + SpeakWhisper, + SpeakYell, + SpeakBroadcast, + SpeakPrivate, + SpeakPrivateRed, + SpeakPrivatePlayerToNpc, + SpeakPrivateNpcToPlayer, + SpeakChannelYellow, + SpeakChannelWhite, + SpeakChannelRed, + SpeakChannelOrange, + SpeakMonsterSay, + SpeakMonsterYell + }; } #endif diff --git a/src/otclient/core/animatedtext.cpp b/src/otclient/core/animatedtext.cpp index 0eac7c0f..e9b35709 100644 --- a/src/otclient/core/animatedtext.cpp +++ b/src/otclient/core/animatedtext.cpp @@ -31,25 +31,26 @@ AnimatedText::AnimatedText() m_font = g_fonts.getFont("verdana-11px-rounded"); } -void AnimatedText::start() +void AnimatedText::draw(const Point& dest, const Rect& visibleRect) { - m_startTime = g_clock.time(); + Point p = dest; + p.x += 20 - m_textSize.width() / 2; + p.y += (-20 * m_animationTimer.ticksElapsed()) / Otc::ANIMATED_TEXT_DURATION; + Rect rect(p, m_textSize); - auto self = asAnimatedText(); - - // schedule removal - g_dispatcher.scheduleEvent([self]() { - g_map.removeThing(self); - }, DURATION); + if(visibleRect.contains(rect)) { + //TODO: cache into a framebuffer + m_font->renderText(m_text, rect, Fw::AlignLeft, m_color); + } } -void AnimatedText::draw(const Point& p, const Rect& visibleRect) +void AnimatedText::startAnimation() { - if(m_font) { - Rect rect = Rect(p + Point(20 - m_textSize.width() / 2, -20.0 * g_clock.timeElapsed(m_startTime) / (DURATION / 1000)), m_textSize); - if(visibleRect.contains(rect)) - m_font->renderText(m_text, rect, Fw::AlignLeft, m_color); - } + m_animationTimer.restart(); + + // schedule removal + auto self = asAnimatedText(); + g_dispatcher.scheduleEvent([self]() { g_map.removeThing(self); }, Otc::ANIMATED_TEXT_DURATION); } void AnimatedText::setColor(int color) @@ -59,7 +60,6 @@ void AnimatedText::setColor(int color) void AnimatedText::setText(const std::string& text) { - if(m_font) - m_textSize = m_font->calculateTextRectSize(text); + m_textSize = m_font->calculateTextRectSize(text); m_text = text; } diff --git a/src/otclient/core/animatedtext.h b/src/otclient/core/animatedtext.h index 8e019d74..cb32d3a8 100644 --- a/src/otclient/core/animatedtext.h +++ b/src/otclient/core/animatedtext.h @@ -25,18 +25,15 @@ #include "thing.h" #include +#include class AnimatedText : public Thing { public: - enum { - DURATION = 1000 - }; - AnimatedText(); - void start(); - void draw(const Point& p, const Rect& visibleRect); + void draw(const Point& dest, const Rect& visibleRect); + void startAnimation(); void setColor(int color); void setText(const std::string& text); @@ -48,7 +45,7 @@ private: Size m_textSize; std::string m_text; Color m_color; - double m_startTime; + Timer m_animationTimer; }; #endif diff --git a/src/otclient/core/creature.cpp b/src/otclient/core/creature.cpp index 93d2edda..8e180035 100644 --- a/src/otclient/core/creature.cpp +++ b/src/otclient/core/creature.cpp @@ -44,6 +44,7 @@ Creature::Creature() : Thing() m_showVolatileSquare = false; m_showStaticSquare = false; m_direction = Otc::South; + m_walkAnimationPhase = 0; m_walking = false; m_walkInterval = 0; m_walkAnimationInterval = 0; @@ -63,16 +64,18 @@ int LEGS_COLOR_UNIFORM = 12; int FEET_COLOR_UNIFORM = 13; int MASK_TEXTURE_UNIFORM = 14; -void Creature::draw(const Point& p, const Rect&) +void Creature::draw(const Point& dest, float scaleFactor, bool animate) { - if(m_showVolatileSquare) { + Point animationOffset = animate ? m_walkOffset : Point(0,0); + + if(m_showVolatileSquare && animate) { g_painter.setColor(m_volatileSquareColor); - g_painter.drawBoundingRect(Rect(p + m_walkOffset - Point(m_type->parameters[ThingType::DisplacementX], m_type->parameters[ThingType::DisplacementY]) + 3, Size(28, 28)), 2); + g_painter.drawBoundingRect(Rect(dest + (animationOffset - getDisplacement() + 3)*scaleFactor, Size(28, 28)*scaleFactor), std::max((int)(2*scaleFactor), 1)); } - if(m_showStaticSquare) { + if(m_showStaticSquare && animate) { g_painter.setColor(m_staticSquareColor); - g_painter.drawBoundingRect(Rect(p + m_walkOffset - Point(m_type->parameters[ThingType::DisplacementX], m_type->parameters[ThingType::DisplacementY]) + 1, Size(32, 32)), 2); + g_painter.drawBoundingRect(Rect(dest + (animationOffset - getDisplacement() + 1)*scaleFactor, Size(Otc::TILE_PIXELS, Otc::TILE_PIXELS)*scaleFactor), std::max((int)(2*scaleFactor), 1)); } g_painter.setColor(Fw::white); @@ -88,12 +91,29 @@ void Creature::draw(const Point& p, const Rect&) outfitProgram->bindUniformLocation(MASK_TEXTURE_UNIFORM, "maskTexture"); } - // Render creature + int xPattern = 0, yPattern = 0, zPattern = 0; + + // outfit is a real creature if(m_outfit.getCategory() == ThingsType::Creature) { - for(m_yPattern = 0; m_yPattern < m_type->dimensions[ThingType::PatternY]; m_yPattern++) { + int animationPhase = animate ? m_walkAnimationPhase : 0; + if(isAnimateAlways()) { + int ticksPerFrame = 1000 / getAnimationPhases(); + animationPhase = (g_clock.ticks() % (ticksPerFrame * getAnimationPhases())) / ticksPerFrame; + } + + // xPattern => creature direction + if(m_direction == Otc::NorthEast || m_direction == Otc::SouthEast) + xPattern = Otc::East; + else if(m_direction == Otc::NorthWest || m_direction == Otc::SouthWest) + xPattern = Otc::West; + else + xPattern = m_direction; + + // yPattern => creature addon + for(yPattern = 0; yPattern < getNumPatternsY(); yPattern++) { // continue if we dont have this addon. - if(m_yPattern > 0 && !(m_outfit.getAddons() & (1 << (m_yPattern-1)))) + if(yPattern > 0 && !(m_outfit.getAddons() & (1 << (yPattern-1)))) continue; g_painter.setCustomProgram(outfitProgram); @@ -104,40 +124,51 @@ void Creature::draw(const Point& p, const Rect&) outfitProgram->setUniformValue(LEGS_COLOR_UNIFORM, m_outfit.getLegsColor()); outfitProgram->setUniformValue(FEET_COLOR_UNIFORM, m_outfit.getFeetColor()); - for(int h = 0; h < m_type->dimensions[ThingType::Height]; h++) { - for(int w = 0; w < m_type->dimensions[ThingType::Width]; w++) { - int spriteId = m_type->getSpriteId(w, h, 0, m_xPattern, m_yPattern, m_zPattern, m_animation); - if(!spriteId) - continue; - TexturePtr spriteTex = g_sprites.getSpriteTexture(spriteId); - if(!spriteTex) - continue; - - if(m_type->dimensions[ThingType::Layers] > 1) { - int maskId = m_type->getSpriteId(w, h, 1, m_xPattern, m_yPattern, m_zPattern, m_animation); - TexturePtr maskTex = g_sprites.getSpriteTexture(maskId); - outfitProgram->setUniformTexture(MASK_TEXTURE_UNIFORM, maskTex, 1); + for(int h = 0; h < getDimensionHeight(); h++) { + for(int w = 0; w < getDimensionWidth(); w++) { + // setup texture outfit mask + TexturePtr maskTex; + if(getLayers() > 1) { + int maskId = getSpriteId(w, h, 1, xPattern, yPattern, zPattern, m_walkAnimationPhase); + maskTex = g_sprites.getSpriteTexture(maskId); } + outfitProgram->setUniformTexture(MASK_TEXTURE_UNIFORM, maskTex, 1); - Rect drawRect(((p + m_walkOffset).x - w*32) - m_type->parameters[ThingType::DisplacementX], - ((p + m_walkOffset).y - h*32) - m_type->parameters[ThingType::DisplacementY], - 32, 32); - g_painter.drawTexturedRect(drawRect, spriteTex); + internalDraw(dest + (animationOffset - Point(w,h)*Otc::TILE_PIXELS)*scaleFactor, + scaleFactor, w, h, xPattern, yPattern, zPattern, 0, animationPhase); } } g_painter.releaseCustomProgram(); } + // outfit is a creature imitating an item or the invisible effect + } else { + int animationPhase = 0; + int animationPhases = getAnimationPhases(); + int animateTicks = Otc::ITEM_TICKS_PER_FRAME; + + // when creature is an effect we cant render the first and last animation phase, + // instead we should loop in the phases between + if(m_outfit.getCategory() == ThingsType::Effect) { + animationPhases = std::max(1, animationPhases-2); + animateTicks = Otc::INVISIBLE_TICKS_PER_FRAME; + } + + if(animationPhases > 1) { + if(animate) + animationPhase = (g_clock.ticks() % (animateTicks * animationPhases)) / animateTicks; + else + animationPhase = animationPhases-1; + } + + if(m_outfit.getCategory() == ThingsType::Effect) + animationPhase = std::min(animationPhase+1, getAnimationPhases()); + + internalDraw(dest + animationOffset*scaleFactor, scaleFactor, 0, 0, 0, animationPhase); } - else if(m_outfit.getCategory() == ThingsType::Item) { - for(int l = 0; l < m_type->dimensions[ThingType::Layers]; l++) - internalDraw(p + m_walkOffset, l); - } - else if(m_outfit.getCategory() == ThingsType::Effect) - internalDraw(p + m_walkOffset, 0); } -void Creature::drawInformation(int x, int y, bool useGray, const Rect& visibleRect) +void Creature::drawInformation(const Point& point, bool useGray, const Rect& parentRect) { Color fillColor = Color(96, 96, 96); @@ -145,16 +176,16 @@ void Creature::drawInformation(int x, int y, bool useGray, const Rect& visibleRe fillColor = m_informationColor; // calculate main rects - Rect backgroundRect = Rect(x-(13.5), y, 27, 4); - backgroundRect.bind(visibleRect); + Rect backgroundRect = Rect(point.x-(13.5), point.y, 27, 4); + backgroundRect.bind(parentRect); - Rect textRect = Rect(x - m_nameSize.width() / 2.0, y-12, m_nameSize); - textRect.bind(visibleRect); + Rect textRect = Rect(point.x - m_nameSize.width() / 2.0, point.y-12, m_nameSize); + textRect.bind(parentRect); // distance them - if(textRect.top() == visibleRect.top()) + if(textRect.top() == parentRect.top()) backgroundRect.moveTop(textRect.top() + 12); - if(backgroundRect.bottom() == visibleRect.bottom()) + if(backgroundRect.bottom() == parentRect.bottom()) textRect.moveTop(backgroundRect.top() - 12); // health rect is based on background rect, so no worries @@ -173,15 +204,15 @@ void Creature::drawInformation(int x, int y, bool useGray, const Rect& visibleRe if(m_skull != Otc::SkullNone && m_skullTexture) { g_painter.setColor(Fw::white); - g_painter.drawTexturedRect(Rect(x + 12, y + 5, m_skullTexture->getSize()), m_skullTexture); + g_painter.drawTexturedRect(Rect(point.x + 12, point.y + 5, m_skullTexture->getSize()), m_skullTexture); } if(m_shield != Otc::ShieldNone && m_shieldTexture && m_showShieldTexture) { g_painter.setColor(Fw::white); - g_painter.drawTexturedRect(Rect(x, y + 5, m_shieldTexture->getSize()), m_shieldTexture); + g_painter.drawTexturedRect(Rect(point.x, point.y + 5, m_shieldTexture->getSize()), m_shieldTexture); } if(m_emblem != Otc::EmblemNone && m_emblemTexture) { g_painter.setColor(Fw::white); - g_painter.drawTexturedRect(Rect(x + 12, y + 16, m_emblemTexture->getSize()), m_emblemTexture); + g_painter.drawTexturedRect(Rect(point.x + 12, point.y + 16, m_emblemTexture->getSize()), m_emblemTexture); } } @@ -209,7 +240,12 @@ void Creature::walk(const Position& oldPos, const Position& newPos) // calculates walk interval float interval = 1000; - int groundSpeed = g_map.getTile(oldPos)->getGroundSpeed(); + int groundSpeed = 0; + + TilePtr oldTile = g_map.getTile(oldPos); + if(oldTile) + groundSpeed = oldTile->getGroundSpeed(); + if(groundSpeed != 0) interval = (1000.0f * groundSpeed) / m_speed; @@ -236,7 +272,7 @@ void Creature::stopWalk() // reset walk animation states m_walkOffset = Point(0,0); - m_animation = 0; + m_walkAnimationPhase = 0; // stops the walk right away terminateWalk(); @@ -248,10 +284,10 @@ void Creature::updateWalkAnimation(int totalPixelsWalked) if(m_outfit.getCategory() != ThingsType::Creature) return; - if(totalPixelsWalked == 32 || totalPixelsWalked == 0 || m_type->dimensions[ThingType::AnimationPhases] <= 1) - m_animation = 0; - else if(m_type->dimensions[ThingType::AnimationPhases] > 1) - m_animation = 1 + ((totalPixelsWalked * 4) / Map::NUM_TILE_PIXELS) % (m_type->dimensions[ThingType::AnimationPhases] - 1); + if(totalPixelsWalked == 32 || totalPixelsWalked == 0 || getAnimationPhases() <= 1) + m_walkAnimationPhase = 0; + else if(getAnimationPhases() > 1) + m_walkAnimationPhase = 1 + ((totalPixelsWalked * 4) / Otc::TILE_PIXELS) % (getAnimationPhases() - 1); } void Creature::updateWalkOffset(int totalPixelsWalked) @@ -268,6 +304,36 @@ void Creature::updateWalkOffset(int totalPixelsWalked) m_walkOffset.x = 32 - totalPixelsWalked; } +void Creature::updateWalkingTile() +{ + // determine new walking tile + TilePtr newWalkingTile; + Rect virtualCreatureRect(Otc::TILE_PIXELS + (m_walkOffset.x - getDisplacementX()), + Otc::TILE_PIXELS + (m_walkOffset.y - getDisplacementY()), + Otc::TILE_PIXELS, Otc::TILE_PIXELS); + for(int xi = -1; xi <= 1 && !newWalkingTile; ++xi) { + for(int yi = -1; yi <= 1 && !newWalkingTile; ++yi) { + Rect virtualTileRect((xi+1)*Otc::TILE_PIXELS, (yi+1)*Otc::TILE_PIXELS, Otc::TILE_PIXELS, Otc::TILE_PIXELS); + + // only render creatures where bottom right is inside tile rect + if(virtualTileRect.contains(virtualCreatureRect.bottomRight())) { + const TilePtr& tile = g_map.getTile(m_position.translated(xi, yi, 0)); + if(!tile) + continue; + newWalkingTile = tile; + } + } + } + + if(newWalkingTile != m_walkingTile) { + if(m_walkingTile) + m_walkingTile->removeWalkingCreature(asCreature()); + if(newWalkingTile) + newWalkingTile->addWalkingCreature(asCreature()); + m_walkingTile = newWalkingTile; + } +} + void Creature::nextWalkUpdate() { // remove any previous scheduled walk updates @@ -295,6 +361,7 @@ void Creature::updateWalk() // update walk animation and offsets updateWalkAnimation(totalPixelsWalked); updateWalkOffset(totalPixelsWalked); + updateWalkingTile(); // terminate walk if(m_walking && m_walkTimer.ticksElapsed() >= m_walkInterval) @@ -315,6 +382,11 @@ void Creature::terminateWalk() m_walkTurnDirection = Otc::InvalidDirection; } + if(m_walkingTile) { + m_walkingTile->removeWalkingCreature(asCreature()); + m_walkingTile = nullptr; + } + m_walking = false; } @@ -358,40 +430,13 @@ void Creature::setHealthPercent(uint8 healthPercent) void Creature::setDirection(Otc::Direction direction) { - if(m_outfit.getCategory() == ThingsType::Creature) { - if(direction == Otc::NorthEast || direction == Otc::SouthEast) - m_xPattern = Otc::East; - else if(direction == Otc::NorthWest || direction == Otc::SouthWest) - m_xPattern = Otc::West; - else - m_xPattern = direction; - } else { - m_xPattern = 0; - } - m_direction = direction; } void Creature::setOutfit(const Outfit& outfit) { m_outfit = outfit; - m_type = getType(); - m_animation = 0; - - if(m_outfit.getCategory() == ThingsType::Effect) { - updateInvisibleAnimation(); - - m_xPattern = 0; - m_yPattern = 0; - } - if(m_outfit.getCategory() == ThingsType::Item) { - m_xPattern = 0; - m_yPattern = 0; - } - - if(m_outfit.getCategory() == ThingsType::Creature && m_type->dimensions[ThingType::Layers] == 1) { - m_outfit.resetClothes(); - } + m_type = g_thingsType.getThingType(outfit.getId(), outfit.getCategory()); } void Creature::setSkull(uint8 skull) @@ -449,26 +494,6 @@ void Creature::addVolatileSquare(uint8 color) }, VOLATILE_SQUARE_DURATION); } -void Creature::updateInvisibleAnimation() -{ - if(!g_game.isOnline() || m_outfit.getCategory() != ThingsType::Effect) - return; - - if(m_animation == 1) - m_animation = 2; - else if(m_animation == 2) - m_animation = 3; - else if(m_animation == 3) - m_animation = 1; - else - m_animation = 1; - - auto self = asCreature(); - g_dispatcher.scheduleEvent([self]() { - self->updateInvisibleAnimation(); - }, INVISIBLE_TICKS); -} - void Creature::updateShield() { m_showShieldTexture = !m_showShieldTexture; @@ -483,8 +508,17 @@ void Creature::updateShield() m_showShieldTexture = true; } -ThingType *Creature::getType() +Point Creature::getDrawOffset() { - return g_thingsType.getThingType(m_outfit.getId(), m_outfit.getCategory()); + Point drawOffset; + if(m_walking) { + if(m_walkingTile) + drawOffset -= Point(1,1) * m_walkingTile->getDrawElevation(); + drawOffset += m_walkOffset; + } else { + const TilePtr& tile = getTile(); + if(tile) + drawOffset -= Point(1,1) * tile->getDrawElevation(); + } + return drawOffset; } - diff --git a/src/otclient/core/creature.h b/src/otclient/core/creature.h index 3b63bcf7..fa9e1008 100644 --- a/src/otclient/core/creature.h +++ b/src/otclient/core/creature.h @@ -34,16 +34,16 @@ class Creature : public Thing public: enum { SHIELD_BLINK_TICKS = 500, - INVISIBLE_TICKS = 500, VOLATILE_SQUARE_DURATION = 1000 }; Creature(); virtual ~Creature() { } - virtual void draw(const Point& p, const Rect&); - void drawInformation(int x, int y, bool useGray, const Rect& visibleRect); + virtual void draw(const Point& dest, float scaleFactor, bool animate); + void drawInformation(const Point& point, bool useGray, const Rect& parentRect); + void setId(uint32 id) { m_id = id; } void setName(const std::string& name); void setHealthPercent(uint8 healthPercent); void setDirection(Otc::Direction direction); @@ -64,6 +64,7 @@ public: void showStaticSquare(const Color& color) { m_showStaticSquare = true; m_staticSquareColor = color; } void hideStaticSquare() { m_showStaticSquare = false; } + uint32 getId() { return m_id; } std::string getName() { return m_name; } uint8 getHealthPercent() { return m_healthPercent; } Otc::Direction getDirection() { return m_direction; } @@ -74,17 +75,15 @@ public: uint8 getShield() { return m_shield; } uint8 getEmblem() { return m_emblem; } bool getPassable() { return m_passable; } + Point getDrawOffset(); + Point getWalkOffset() { return m_walkOffset; } - void updateInvisibleAnimation(); void updateShield(); - ThingType *getType(); - // walk related void turn(Otc::Direction direction); virtual void walk(const Position& oldPos, const Position& newPos); virtual void stopWalk(); - Point getWalkOffset() { return m_walkOffset; } bool isWalking() { return m_walking; } @@ -93,10 +92,12 @@ public: protected: virtual void updateWalkAnimation(int totalPixelsWalked); virtual void updateWalkOffset(int totalPixelsWalked); + void updateWalkingTile(); virtual void nextWalkUpdate(); virtual void updateWalk(); virtual void terminateWalk(); + uint32 m_id; std::string m_name; Size m_nameSize; uint8 m_healthPercent; @@ -115,7 +116,9 @@ protected: Color m_informationColor; // walk related + int m_walkAnimationPhase; Timer m_walkTimer; + TilePtr m_walkingTile; int m_walkInterval; int m_walkAnimationInterval; bool m_walking; diff --git a/src/otclient/core/declarations.h b/src/otclient/core/declarations.h index b2375c70..a3e29eeb 100644 --- a/src/otclient/core/declarations.h +++ b/src/otclient/core/declarations.h @@ -25,6 +25,8 @@ #include +class Map; +class MapView; class Tile; class Thing; class Item; @@ -38,6 +40,7 @@ class Missile; class AnimatedText; class StaticText; +typedef std::shared_ptr MapViewPtr; typedef std::shared_ptr TilePtr; typedef std::shared_ptr ThingPtr; typedef std::shared_ptr ItemPtr; diff --git a/src/otclient/core/effect.cpp b/src/otclient/core/effect.cpp index 3f6eff8e..54b12596 100644 --- a/src/otclient/core/effect.cpp +++ b/src/otclient/core/effect.cpp @@ -27,51 +27,28 @@ #include #include -Effect::Effect() : Thing() +void Effect::draw(const Point& dest, float scaleFactor, bool animate) { - m_animationStartTicks = 0; -} - -void Effect::start() -{ - m_animationStartTicks = g_clock.ticks(); + if(m_id == 0) + return; - auto self = asEffect(); - - // schedule update - if(getAnimationPhases() > 1) { - g_dispatcher.scheduleEvent([self]() { - self->updateAnimation(); - }, TICKS_PER_FRAME); - } - - // schedule removal - g_dispatcher.scheduleEvent([self]() { - g_map.removeThing(self); - }, TICKS_PER_FRAME * getAnimationPhases()); + int animationPhase = 0; + if(animate) + animationPhase = std::min((int)(m_animationTimer.ticksElapsed() / Otc::EFFECT_TICKS_PER_FRAME), getAnimationPhases() - 1); + internalDraw(dest, scaleFactor, 0, 0, 0, animationPhase); } -void Effect::draw(const Point& p, const Rect&) +void Effect::startAnimation() { - internalDraw(p, 0); -} - -void Effect::updateAnimation() -{ - int animationPhase = (g_clock.ticks() - m_animationStartTicks) / TICKS_PER_FRAME; + m_animationTimer.restart(); - if(animationPhase < getAnimationPhases()) - m_animation = animationPhase; - - if(animationPhase < getAnimationPhases() - 1) { - auto self = asEffect(); - g_dispatcher.scheduleEvent([self]() { - self->updateAnimation(); - }, TICKS_PER_FRAME); - } + // schedule removal + auto self = asEffect(); + g_dispatcher.scheduleEvent([self]() { g_map.removeThing(self); }, Otc::EFFECT_TICKS_PER_FRAME * getAnimationPhases()); } -ThingType *Effect::getType() +void Effect::setId(uint32 id) { - return g_thingsType.getThingType(m_id, ThingsType::Effect); + m_id = id; + m_type = g_thingsType.getThingType(m_id, ThingsType::Effect); } diff --git a/src/otclient/core/effect.h b/src/otclient/core/effect.h index d305f55d..fb1794b5 100644 --- a/src/otclient/core/effect.h +++ b/src/otclient/core/effect.h @@ -24,28 +24,23 @@ #define EFFECT_H #include +#include #include "thing.h" class Effect : public Thing { public: - enum { - TICKS_PER_FRAME = 75 - }; + void draw(const Point& dest, float scaleFactor, bool animate); - Effect(); - - void draw(const Point& p, const Rect&); - - void start(); - void updateAnimation(); - - ThingType *getType(); + void setId(uint32 id); + void startAnimation(); + uint32 getId() { return m_id; } EffectPtr asEffect() { return std::static_pointer_cast(shared_from_this()); } private: - ticks_t m_animationStartTicks; + Timer m_animationTimer; + uint16 m_id; }; #endif diff --git a/src/otclient/core/game.cpp b/src/otclient/core/game.cpp index cd08b1b7..044b9a0b 100644 --- a/src/otclient/core/game.cpp +++ b/src/otclient/core/game.cpp @@ -94,7 +94,7 @@ void Game::processLogout() m_protocolGame = nullptr; } - g_map.clean(); + g_map.save(); } void Game::processDeath() @@ -172,9 +172,9 @@ void Game::processTextMessage(const std::string& type, const std::string& messag g_lua.callGlobalField("Game","onTextMessage", type, message); } -void Game::processCreatureSpeak(const std::string& name, int level, const std::string& type, const std::string& message, int channelId, const Position& creaturePos) +void Game::processCreatureSpeak(const std::string& name, int level, Otc::SpeakType type, const std::string& message, int channelId, const Position& creaturePos) { - if(creaturePos.isValid() && (type == "say" || type == "whisper" || type == "yell" || type == "monsterSay" || type == "monsterYell")) { + if(creaturePos.isValid() && (type == Otc::SpeakSay || type == Otc::SpeakWhisper || type == Otc::SpeakYell || type == Otc::SpeakMonsterSay || type == Otc::SpeakMonsterYell)) { StaticTextPtr staticText = StaticTextPtr(new StaticText); staticText->addMessage(name, type, message); g_map.addThing(staticText, creaturePos); @@ -186,7 +186,7 @@ void Game::processCreatureSpeak(const std::string& name, int level, const std::s void Game::processContainerAddItem(int containerId, const ItemPtr& item) { if(item) - item->setPos(Position(65535, containerId + 0x40, 0)); + item->setPosition(Position(65535, containerId + 0x40, 0)); g_lua.callGlobalField("Game", "onContainerAddItem", containerId, item); } @@ -194,7 +194,7 @@ void Game::processContainerAddItem(int containerId, const ItemPtr& item) void Game::processInventoryChange(int slot, const ItemPtr& item) { if(item) - item->setPos(Position(65535, slot, 0)); + item->setPosition(Position(65535, slot, 0)); g_lua.callGlobalField("Game","onInventoryChange", slot, item); } @@ -202,7 +202,7 @@ void Game::processInventoryChange(int slot, const ItemPtr& item) void Game::processCreatureMove(const CreaturePtr& creature, const Position& oldPos, const Position& newPos) { // animate walk - if(oldPos.isInRange(newPos, 1, 1, 0)) + if(oldPos.isInRange(newPos, 1, 1)) creature->walk(oldPos, newPos); } @@ -237,16 +237,8 @@ void Game::walk(Otc::Direction direction) if(!m_localPlayer->canWalk(direction)) return; - - // TODO: restore check for blockable tiles - /* - if(toTile && !toTile->isWalkable() && !fromTile->getElevation() >= 3) { - g_game.processTextMessage("statusSmall", "Sorry, not possible."); - return false; - }*/ - - // only do prewalk to walkable tiles - TilePtr toTile = g_map.getTile(m_localPlayer->getPos() + Position::getPosFromDirection(direction)); + // only do prewalks to walkable tiles + TilePtr toTile = g_map.getTile(m_localPlayer->getPosition().translatedToDirection(direction)); if(toTile && toTile->isWalkable()) m_localPlayer->preWalk(direction); else @@ -316,7 +308,7 @@ void Game::look(const ThingPtr& thing) int stackpos = getThingStackpos(thing); if(stackpos != -1) - m_protocolGame->sendLookAt(thing->getPos(), thing->getId(), stackpos); + m_protocolGame->sendLookAt(thing->getPosition(), thing->getId(), stackpos); } void Game::open(const ThingPtr& thing, int containerId) @@ -326,7 +318,7 @@ void Game::open(const ThingPtr& thing, int containerId) int stackpos = getThingStackpos(thing); if(stackpos != -1) - m_protocolGame->sendUseItem(thing->getPos(), thing->getId(), stackpos, containerId); + m_protocolGame->sendUseItem(thing->getPosition(), thing->getId(), stackpos, containerId); } void Game::use(const ThingPtr& thing) @@ -338,7 +330,7 @@ void Game::use(const ThingPtr& thing) int stackpos = getThingStackpos(thing); if(stackpos != -1) - m_protocolGame->sendUseItem(thing->getPos(), thing->getId(), stackpos, 0); + m_protocolGame->sendUseItem(thing->getPosition(), thing->getId(), stackpos, 0); } void Game::useWith(const ThingPtr& fromThing, const ThingPtr& toThing) @@ -346,7 +338,7 @@ void Game::useWith(const ThingPtr& fromThing, const ThingPtr& toThing) if(!isOnline() || !fromThing || !toThing || !checkBotProtection()) return; - Position pos = fromThing->getPos(); + Position pos = fromThing->getPosition(); int fromStackpos = getThingStackpos(fromThing); if(fromStackpos == -1) return; @@ -360,7 +352,7 @@ void Game::useWith(const ThingPtr& fromThing, const ThingPtr& toThing) if(toStackpos == -1) return; - m_protocolGame->sendUseItemEx(pos, fromThing->getId(), fromStackpos, toThing->getPos(), toThing->getId(), toStackpos); + m_protocolGame->sendUseItemEx(pos, fromThing->getId(), fromStackpos, toThing->getPosition(), toThing->getId(), toStackpos); } } @@ -379,13 +371,13 @@ void Game::useInventoryItem(int itemId, const ThingPtr& toThing) if(CreaturePtr creature = toThing->asCreature()) { m_protocolGame->sendUseOnCreature(pos, itemId, 0, creature->getId()); } else { - m_protocolGame->sendUseItemEx(pos, itemId, 0, toThing->getPos(), toThing->getId(), toStackpos); + m_protocolGame->sendUseItemEx(pos, itemId, 0, toThing->getPosition(), toThing->getId(), toStackpos); } } void Game::move(const ThingPtr& thing, const Position& toPos, int count) { - if(!isOnline() || !thing || !checkBotProtection() || thing->getPos() == toPos || count <= 0) + if(!isOnline() || !thing || !checkBotProtection() || thing->getPosition() == toPos || count <= 0) return; m_localPlayer->lockWalk(); @@ -394,7 +386,7 @@ void Game::move(const ThingPtr& thing, const Position& toPos, int count) if(stackpos == -1) return; - m_protocolGame->sendThrow(thing->getPos(), thing->getId(), stackpos, toPos, count); + m_protocolGame->sendThrow(thing->getPosition(), thing->getId(), stackpos, toPos, count); } void Game::attack(const CreaturePtr& creature) @@ -433,6 +425,9 @@ void Game::follow(const CreaturePtr& creature) void Game::cancelFollow() { + if(!isOnline() || !checkBotProtection()) + return; + m_localPlayer->setFollowingCreature(nullptr); m_protocolGame->sendFollow(0); } @@ -444,16 +439,15 @@ void Game::rotate(const ThingPtr& thing) int stackpos = getThingStackpos(thing); if(stackpos != -1) - m_protocolGame->sendRotateItem(thing->getPos(), thing->getId(), stackpos); + m_protocolGame->sendRotateItem(thing->getPosition(), thing->getId(), stackpos); } //TODO: move this to Thing class int Game::getThingStackpos(const ThingPtr& thing) { // thing is at map - if(thing->getPos().x != 65535) { - TilePtr tile = g_map.getTile(thing->getPos()); - if(tile) + if(thing->getPosition().x != 65535) { + if(TilePtr tile = g_map.getTile(thing->getPosition())) return tile->getThingStackpos(thing); else { logError("could not get tile"); @@ -467,21 +461,28 @@ int Game::getThingStackpos(const ThingPtr& thing) void Game::talk(const std::string& message) { - talkChannel("say", 0, message); + talkChannel(Otc::SpeakSay, 0, message); +} + +void Game::talkChannel(Otc::SpeakType speakType, int channelId, const std::string& message) +{ + if(!isOnline() || !checkBotProtection()) + return; + m_protocolGame->sendTalk(speakType, channelId, "", message); } -void Game::talkChannel(const std::string& speakTypeDesc, int channelId, const std::string& message) +void Game::talkPrivate(Otc::SpeakType speakType, const std::string& receiver, const std::string& message) { if(!isOnline() || !checkBotProtection()) return; - m_protocolGame->sendTalk(speakTypeDesc, channelId, "", message); + m_protocolGame->sendTalk(speakType, 0, receiver, message); } -void Game::talkPrivate(const std::string& speakTypeDesc, const std::string& receiver, const std::string& message) +void Game::openPrivateChannel(const std::string& receiver) { if(!isOnline() || !checkBotProtection()) return; - m_protocolGame->sendTalk(speakTypeDesc, 0, receiver, message); + m_protocolGame->sendOpenPrivateChannel(receiver); } void Game::requestChannels() diff --git a/src/otclient/core/game.h b/src/otclient/core/game.h index 718dbb7f..9d83c81f 100644 --- a/src/otclient/core/game.h +++ b/src/otclient/core/game.h @@ -52,7 +52,7 @@ public: double magicLevel, double magicLevelPercent, double soul, double stamina); void processTextMessage(const std::string& type, const std::string& message); - void processCreatureSpeak(const std::string& name, int level, const std::string& type, const std::string& message, int channelId, const Position& creaturePos); + void processCreatureSpeak(const std::string& name, int level, Otc::SpeakType type, const std::string& message, int channelId, const Position& creaturePos); void processContainerAddItem(int containerId, const ItemPtr& item); void processInventoryChange(int slot, const ItemPtr& item); void processCreatureMove(const CreaturePtr& creature, const Position& oldPos, const Position& newPos); @@ -82,8 +82,9 @@ public: // talk related void talk(const std::string& message); - void talkChannel(const std::string& speakTypeDesc, int channelId, const std::string& message); - void talkPrivate(const std::string& speakTypeDesc, const std::string& receiver, const std::string& message); + void talkChannel(Otc::SpeakType speakType, int channelId, const std::string& message); + void talkPrivate(Otc::SpeakType speakType, const std::string& receiver, const std::string& message); + void openPrivateChannel(const std::string& receiver); void requestChannels(); void joinChannel(int channelId); void leaveChannel(int channelId); diff --git a/src/otclient/core/item.cpp b/src/otclient/core/item.cpp index a2ca17a8..17f26cc8 100644 --- a/src/otclient/core/item.cpp +++ b/src/otclient/core/item.cpp @@ -24,143 +24,181 @@ #include "thingstype.h" #include "spritemanager.h" #include "thing.h" +#include "tile.h" #include #include +#include +#include +#include Item::Item() : Thing() { - m_data = 1; + m_id = 0; + m_countOrSubType = 0; } ItemPtr Item::create(int id) { + if(id < g_thingsType.getFirstItemId() || id > g_thingsType.getMaxItemid()) { + logTraceError("invalid item id ", id); + return nullptr; + } ItemPtr item = ItemPtr(new Item); item->setId(id); return item; } -void Item::draw(const Point& p, const Rect&) -{ - if(m_type->dimensions[ThingType::AnimationPhases] > 1) - m_animation = (g_clock.ticks() % (TICKS_PER_FRAME * m_type->dimensions[ThingType::AnimationPhases])) / TICKS_PER_FRAME; - - for(int l = 0; l < m_type->dimensions[ThingType::Layers]; l++) - internalDraw(p, l); -} +PainterShaderProgramPtr itemProgram; -void Item::setPos(const Position& position) +void Item::draw(const Point& dest, float scaleFactor, bool animate) { - if(m_type->properties[ThingType::IsGround]) { - m_xPattern = position.x % m_type->dimensions[ThingType::PatternX]; - m_yPattern = position.y % m_type->dimensions[ThingType::PatternY]; - m_zPattern = position.z % m_type->dimensions[ThingType::PatternZ]; - } + if(m_id == 0) + return; - Thing::setPos(position); -} + // determine animation phase + int animationPhase = 0; + if(getAnimationPhases() > 1) { + if(animate) + animationPhase = (g_clock.ticks() % (Otc::ITEM_TICKS_PER_FRAME * getAnimationPhases())) / Otc::ITEM_TICKS_PER_FRAME; + else + animationPhase = getAnimationPhases()-1; + } -void Item::setData(int data) -{ - if(m_type->properties[ThingType::IsStackable] && m_type->dimensions[ThingType::PatternX] == 4 && m_type->dimensions[ThingType::PatternY] == 2) { - if(data < 5) { - m_xPattern = data-1; - m_yPattern = 0; - } - else if(data < 10) { - m_xPattern = 0; - m_yPattern = 1; + // determine x,y,z patterns + int xPattern = 0, yPattern = 0, zPattern = 0; + if(isGround()) { + xPattern = m_position.x % getNumPatternsX(); + yPattern = m_position.y % getNumPatternsY(); + zPattern = m_position.z % getNumPatternsZ(); + } else if(isStackable() && getNumPatternsX() == 4 && getNumPatternsY() == 2) { + if(m_countOrSubType < 5) { + xPattern = m_countOrSubType-1; + yPattern = 0; + } else if(m_countOrSubType < 10) { + xPattern = 0; + yPattern = 1; + } else if(m_countOrSubType < 25) { + xPattern = 1; + yPattern = 1; + } else if(m_countOrSubType < 50) { + xPattern = 2; + yPattern = 1; + } else if(m_countOrSubType <= 100) { + xPattern = 3; + yPattern = 1; } - else if(data < 25) { - m_xPattern = 1; - m_yPattern = 1; + } else if(isHangable()) { + const TilePtr& tile = getTile(); + if(tile) { + if(tile->mustHookSouth()) + xPattern = getNumPatternsX() >= 2 ? 1 : 0; + else if(tile->mustHookSouth()) + xPattern = getNumPatternsX() >= 3 ? 2 : 0; } - else if(data < 50) { - m_xPattern = 2; - m_yPattern = 1; - } - else if(data <= 100) { - m_xPattern = 3; - m_yPattern = 1; + } else if(isFluid() || isFluidContainer()) { + int color = Otc::FluidTransparent; + switch(m_countOrSubType) { + case Otc::FluidNone: + color = Otc::FluidTransparent; + break; + case Otc::FluidWater: + color = Otc::FluidBlue; + break; + case Otc::FluidMana: + color = Otc::FluidPurple; + break; + case Otc::FluidBeer: + color = Otc::FluidBrown; + break; + case Otc::FluidOil: + color = Otc::FluidBrown; + break; + case Otc::FluidBlood: + color = Otc::FluidRed; + break; + case Otc::FluidSlime: + color = Otc::FluidGreen; + break; + case Otc::FluidMud: + color = Otc::FluidBrown; + break; + case Otc::FluidLemonade: + color = Otc::FluidYellow; + break; + case Otc::FluidMilk: + color = Otc::FluidWhite; + break; + case Otc::FluidWine: + color = Otc::FluidPurple; + break; + case Otc::FluidHealth: + color = Otc::FluidRed; + break; + case Otc::FluidUrine: + color = Otc::FluidYellow; + break; + case Otc::FluidRum: + color = Otc::FluidBrown; + break; + case Otc::FluidFruidJuice: + color = Otc::FluidYellow; + break; + case Otc::FluidCoconutMilk: + color = Otc::FluidWhite; + break; + case Otc::FluidTea: + color = Otc::FluidBrown; + break; + case Otc::FluidMead: + color = Otc::FluidBrown; + break; + default: + color = Otc::FluidTransparent; + break; } + + xPattern = (color % 4) % getNumPatternsX(); + yPattern = (color / 4) % getNumPatternsY(); } - else if(m_type->properties[ThingType::IsHangable]) { - if(m_type->properties[ThingType::HookSouth]) { - m_xPattern = m_type->dimensions[ThingType::PatternX] >= 2 ? 1 : 0; - } - else if(m_type->properties[ThingType::HookEast]) { - m_xPattern = m_type->dimensions[ThingType::PatternX] >= 3 ? 2 : 0; - } + + // setup item drawing shader + if(!itemProgram) { + itemProgram = PainterShaderProgramPtr(new PainterShaderProgram); + itemProgram->addShaderFromSourceCode(Shader::Vertex, glslMainWithTexCoordsVertexShader + glslPositionOnlyVertexShader); + itemProgram->addShaderFromSourceFile(Shader::Fragment, "/game_shaders/item.frag"); + assert(itemProgram->link()); } - else if(m_type->properties[ThingType::IsFluid] || m_type->properties[ThingType::IsFluidContainer]) { - int color = Otc::FluidTransparent; - switch(data) { - case Otc::FluidNone: - color = Otc::FluidTransparent; - break; - case Otc::FluidWater: - color = Otc::FluidBlue; - break; - case Otc::FluidMana: - color = Otc::FluidPurple; - break; - case Otc::FluidBeer: - color = Otc::FluidBrown; - break; - case Otc::FluidOil: - color = Otc::FluidBrown; - break; - case Otc::FluidBlood: - color = Otc::FluidRed; - break; - case Otc::FluidSlime: - color = Otc::FluidGreen; - break; - case Otc::FluidMud: - color = Otc::FluidBrown; - break; - case Otc::FluidLemonade: - color = Otc::FluidYellow; - break; - case Otc::FluidMilk: - color = Otc::FluidWhite; - break; - case Otc::FluidWine: - color = Otc::FluidPurple; - break; - case Otc::FluidHealth: - color = Otc::FluidRed; - break; - case Otc::FluidUrine: - color = Otc::FluidYellow; - break; - case Otc::FluidRum: - color = Otc::FluidBrown; - break; - case Otc::FluidFruidJuice: - color = Otc::FluidYellow; - break; - case Otc::FluidCoconutMilk: - color = Otc::FluidWhite; - break; - case Otc::FluidTea: - color = Otc::FluidBrown; - break; - case Otc::FluidMead: - color = Otc::FluidBrown; - break; - default: - color = Otc::FluidTransparent; - break; - } + g_painter.setCustomProgram(itemProgram); + + // now we can draw the item + internalDraw(dest, scaleFactor, xPattern, yPattern, zPattern, animationPhase); - m_xPattern = (color % 4) % m_type->dimensions[ThingType::PatternX]; - m_yPattern = (color / 4) % m_type->dimensions[ThingType::PatternY]; + // release draw shader + g_painter.releaseCustomProgram(); +} + +void Item::setId(uint32 id) +{ + if(id < g_thingsType.getFirstItemId() || id > g_thingsType.getMaxItemid()) { + logTraceError("invalid item id ", id); + return; } + m_id = id; + m_type = g_thingsType.getThingType(m_id, ThingsType::Item); +} - m_data = data; +int Item::getCount() +{ + if(isStackable()) + return m_countOrSubType; + else + return 1; } -ThingType *Item::getType() +int Item::getSubType() { - return g_thingsType.getThingType(m_id, ThingsType::Item); + if(isFluid() || isFluidContainer()) + return m_countOrSubType; + else + return 0; } diff --git a/src/otclient/core/item.h b/src/otclient/core/item.h index 70a2c288..38000e6f 100644 --- a/src/otclient/core/item.h +++ b/src/otclient/core/item.h @@ -33,22 +33,23 @@ public: static ItemPtr create(int id); - enum { - TICKS_PER_FRAME = 500 - }; + void draw(const Point& dest, float scaleFactor, bool animate); - void draw(const Point& p, const Rect&); + void setId(uint32 id); + void setCountOrSubType(uint8 value) { m_countOrSubType = value; } + void setCount(int count) { setCountOrSubType(count); } + void setSubType(int subType) { setCountOrSubType(subType); } - void setPos(const Position &position); - void setData(int data); - - int getData() { return m_data; } - ThingType *getType(); + uint8 getCountOrSubType() { return m_countOrSubType; } + int getSubType(); + int getCount(); + uint32 getId() { return m_id; } ItemPtr asItem() { return std::static_pointer_cast(shared_from_this()); } private: - int m_data; + uint16 m_id; + uint8 m_countOrSubType; }; #endif diff --git a/src/otclient/core/localplayer.cpp b/src/otclient/core/localplayer.cpp index 38835ee9..6a5b25a9 100644 --- a/src/otclient/core/localplayer.cpp +++ b/src/otclient/core/localplayer.cpp @@ -70,11 +70,11 @@ void LocalPlayer::walk(const Position& oldPos, const Position& newPos) void LocalPlayer::preWalk(Otc::Direction direction) { // start walking to direction - Position newPos = m_pos + Position::getPosFromDirection(direction); + Position newPos = m_position.translatedToDirection(direction); m_preWalking = true; m_lastPrewalkDone = false; m_lastPrewalkDestionation = newPos; - Creature::walk(m_pos, newPos); + Creature::walk(m_position, newPos); } bool LocalPlayer::canWalk(Otc::Direction direction) @@ -145,6 +145,7 @@ void LocalPlayer::updateWalk() // update walk animation and offsets updateWalkAnimation(totalPixelsWalked); updateWalkOffset(totalPixelsWalked); + updateWalkingTile(); // terminate walk only when client and server side walk are complated if(m_walking && !m_preWalking && m_walkTimer.ticksElapsed() >= m_walkInterval) diff --git a/src/otclient/core/map.cpp b/src/otclient/core/map.cpp index b269faea..a98e682c 100644 --- a/src/otclient/core/map.cpp +++ b/src/otclient/core/map.cpp @@ -28,254 +28,122 @@ #include "missile.h" #include "statictext.h" -#include -#include -#include -#include -#include +#include +#include "mapview.h" +#include Map g_map; -Map::Map() +void Map::addMapView(const MapViewPtr& mapView) { - setVisibleSize(Size(MAP_VISIBLE_WIDTH, MAP_VISIBLE_HEIGHT)); + m_mapViews.push_back(mapView); } -void Map::draw(const Rect& rect) +void Map::removeMapView(const MapViewPtr& mapView) { - if(!m_framebuffer) { - Size fboSize(m_visibleSize.width() * NUM_TILE_PIXELS, m_visibleSize.height() * NUM_TILE_PIXELS); - m_framebuffer = FrameBufferPtr(new FrameBuffer(fboSize)); - m_framebuffer->setClearColor(Fw::black); - + auto it = std::find(m_mapViews.begin(), m_mapViews.end(), mapView); + if(it != m_mapViews.end()) + m_mapViews.erase(it); +} - m_shaderProgram = PainterShaderProgramPtr(new PainterShaderProgram); - m_shaderProgram->addShaderFromSourceCode(Shader::Vertex, glslMainWithTexCoordsVertexShader + glslPositionOnlyVertexShader); - m_shaderProgram->addShaderFromSourceFile(Shader::Fragment, "/game_shaders/map.frag"); - assert(m_shaderProgram->link()); - } +void Map::notificateTileUpdateToMapViews(const Position& pos) +{ + for(const MapViewPtr& mapView : m_mapViews) + mapView->onTileUpdate(pos); +} - g_painter.setColor(Fw::white); - m_framebuffer->bind(); +void Map::load() +{ + if(!g_resources.fileExists("/map.otcmap")) + return; - // draw offsets - LocalPlayerPtr localPlayer = g_game.getLocalPlayer(); - if(localPlayer) - m_drawOffset = localPlayer->getWalkOffset(); - - //TODO: cache first/last visible floor - // draw from bottom floors to top floors - int firstFloor = getFirstVisibleFloor(); - const int lastFloor = MAX_Z-1; - for(int iz = lastFloor; iz >= firstFloor; --iz) { - // draw tiles like linus pauling's rule order - const int numDiagonals = m_size.width() + m_size.height() - 1; - for(int diagonal = 0; diagonal < numDiagonals; ++diagonal) { - // loop through / diagonal tiles - for(int ix = std::min(diagonal, m_size.width() - 1), iy = std::max(diagonal - m_size.width(), 0); ix >= 0 && iy < m_size.height(); --ix, ++iy) { - // position on current floor - Position tilePos(m_centralPosition.x + (ix - m_centralOffset.x), m_centralPosition.y + (iy - m_centralOffset.y), m_centralPosition.z); - // adjust tilePos to the wanted floor - tilePos.perspectiveUp(m_centralPosition.z - iz); - //TODO: cache visible tiles, m_tiles[] has a high cost (50% fps decrease) - if(const TilePtr& tile = m_tiles[tilePos]) { - // skip tiles that are behind another tile - //if(isCompletlyCovered(tilePos, firstFloor)) - // continue; - tile->draw(positionTo2D(tilePos) - m_drawOffset, rect); - } + std::stringstream in; + g_resources.loadFile("/map.otcmap", in); + + while(!in.eof()) { + Position pos; + in.read((char*)&pos, sizeof(pos)); + + uint16 id; + in.read((char*)&id, sizeof(id)); + while(id != 0xFFFF) { + ItemPtr item = Item::create(id); + if(item->isStackable() || item->isFluidContainer() || item->isFluid()) { + uint8 countOrSubType; + in.read((char*)&countOrSubType, sizeof(countOrSubType)); + item->setCountOrSubType(countOrSubType); } - } - - // after drawing all tiles, draw shots - for(const MissilePtr& shot : m_missilesAtFloor[iz]) { - Position missilePos = shot->getPos(); - shot->draw(positionTo2D(missilePos) - m_drawOffset, rect); + addThing(item, pos, 255); + in.read((char*)&id, sizeof(id)); } } +} - m_framebuffer->release(); - - - g_painter.setCustomProgram(m_shaderProgram); - g_painter.setColor(Fw::white); - m_framebuffer->draw(rect); - g_painter.releaseCustomProgram(); - - // calculate stretch factor - float horizontalStretchFactor = rect.width() / (float)(m_visibleSize.width() * NUM_TILE_PIXELS); - float verticalStretchFactor = rect.height() / (float)(m_visibleSize.height() * NUM_TILE_PIXELS); - - // draw player names and health bars - //TODO: this must be cached with creature walks - for(int x = 0; x < m_visibleSize.width(); ++x) { - for(int y = 0; y < m_visibleSize.height(); ++y) { - Position tilePos = Position(m_centralPosition.x + (x - m_centralOffset.x + 1), m_centralPosition.y + (y - m_centralOffset.y + 1), m_centralPosition.z); - if(const TilePtr& tile = m_tiles[tilePos]) { - auto creatures = tile->getCreatures(); - - if(creatures.size() == 0) - continue; - - for(const CreaturePtr& creature : creatures) { - Point p((m_centralOffset.x - 1 + (tilePos.x - m_centralPosition.x))*NUM_TILE_PIXELS + 10 - tile->getDrawElevation(), - (m_centralOffset.y - 1 + (tilePos.y - m_centralPosition.y))*NUM_TILE_PIXELS - 10 - tile->getDrawElevation()); - - if(creature != localPlayer) { - p += creature->getWalkOffset() - m_drawOffset; - } - - creature->drawInformation(rect.x() + p.x*horizontalStretchFactor, rect.y() + p.y*verticalStretchFactor, isCovered(tilePos, firstFloor), rect); +void Map::save() +{ + std::stringstream out; + + for(auto& pair : m_tiles) { + Position pos = pair.first; + TilePtr tile = pair.second; + if(!tile || tile->isEmpty()) + continue; + out.write((char*)&pos, sizeof(pos)); + uint16 id; + for(const ThingPtr& thing : tile->getThings()) { + if(ItemPtr item = thing->asItem()) { + id = item->getId(); + out.write((char*)&id, sizeof(id)); + if(item->isStackable() || item->isFluidContainer() || item->isFluid()) { + uint8 countOrSubType = item->getCountOrSubType(); + out.write((char*)&countOrSubType, sizeof(countOrSubType)); } } } + id = 0xFFFF; + out.write((char*)&id, sizeof(id)); } - // draw static text - for(auto it = m_staticTexts.begin(), end = m_staticTexts.end(); it != end; ++it) { - Point pos = positionTo2D((*it)->getPos()) - m_drawOffset; - pos.x *= horizontalStretchFactor; - pos.y *= verticalStretchFactor; - (*it)->draw(rect.topLeft() + pos, rect); - } - - // draw animated text - for(auto it = m_animatedTexts.begin(), end = m_animatedTexts.end(); it != end; ++it) { - Point pos = positionTo2D((*it)->getPos()) - m_drawOffset; - pos.x *= horizontalStretchFactor; - pos.y *= verticalStretchFactor; - (*it)->draw(rect.topLeft() + pos, rect); - } + g_resources.saveFile("/map.otcmap", out); } void Map::clean() { m_tiles.clear(); - m_creatures.clear(); - for(int i=0;i= firstFloor) { - if(TilePtr tile = m_tiles[upperPos]) { - if(ThingPtr firstThing = tile->getThing(0)) { - ThingType *type = firstThing->getType(); - if((type->properties[ThingType::IsGround] || type->properties[ThingType::IsOnBottom]) && !type->properties[ThingType::DontHide]) { - firstFloor = upperPos.z + 1; - break; - } - } - } - if(TilePtr tile = m_tiles[perspectivePos]) { - if(ThingPtr firstThing = tile->getThing(0)) { - ThingType *type = firstThing->getType(); - if((type->properties[ThingType::IsGround] || type->properties[ThingType::IsOnBottom]) && !type->properties[ThingType::DontHide]) { - firstFloor = perspectivePos.z + 1; - break; - } - } - } - perspectivePos.perspectiveUp(); - upperPos.up(); - } - } - } - } - return firstFloor; -} - -bool Map::isLookPossible(const Position& pos) -{ - TilePtr tile = m_tiles[pos]; - if(tile) - return tile->isLookPossible(); - return true; -} - -bool Map::isCovered(const Position& pos, int firstFloor) -{ - Position tilePos = pos; - tilePos.perspectiveUp(); - while(tilePos.z >= firstFloor) { - TilePtr tile = m_tiles[tilePos]; - if(tile && tile->isFullGround()) - return true; - tilePos.perspectiveUp(); - } - return false; -} - -bool Map::isCompletlyCovered(const Position& pos, int firstFloor) -{ - Position tilePos = pos; - tilePos.perspectiveUp(); - while(tilePos.z >= firstFloor) { - bool covered = true; - for(int x=0;x<2;++x) { - for(int y=0;y<2;++y) { - TilePtr tile = m_tiles[tilePos + Position(-x, -y, 0)]; - if(!tile || !tile->isFullyOpaque()) { - covered = false; - break; - } - } - } - if(covered) - return true; - tilePos.perspectiveUp(); - } - return false; -} - void Map::addThing(const ThingPtr& thing, const Position& pos, int stackPos) { if(!thing) return; - Position oldPos = thing->getPos(); - bool teleport = false; - if(oldPos.isValid() && !oldPos.isInRange(pos,1,1,0)) - teleport = true; - - TilePtr tile = getTile(pos); + TilePtr tile = getOrCreateTile(pos); if(CreaturePtr creature = thing->asCreature()) { + Position oldPos = thing->getPosition(); tile->addThing(thing, stackPos); - m_creatures[creature->getId()] = creature; - if(teleport) + if(oldPos.isValid() && !oldPos.isInRange(pos,1,1)) g_game.processCreatureTeleport(creature); - } - else if(MissilePtr shot = thing->asMissile()) { - m_missilesAtFloor[shot->getPos().z].push_back(shot); - } - else if(AnimatedTextPtr animatedText = thing->asAnimatedText()) { + } else if(MissilePtr missile = thing->asMissile()) { + m_floorMissiles[pos.z].push_back(missile); + } else if(AnimatedTextPtr animatedText = thing->asAnimatedText()) { m_animatedTexts.push_back(animatedText); - } - else if(StaticTextPtr staticText = thing->asStaticText()) { + } else if(StaticTextPtr staticText = thing->asStaticText()) { bool mustAdd = true; for(auto it = m_staticTexts.begin(), end = m_staticTexts.end(); it != end; ++it) { StaticTextPtr cStaticText = *it; - if(cStaticText->getPos() == pos) { + if(cStaticText->getPosition() == pos) { // try to combine messages if(cStaticText->addMessage(staticText->getName(), staticText->getMessageType(), staticText->getFirstMessage())) { mustAdd = false; break; - } - else { + } else { // must add another message and rearrenge current } } @@ -284,113 +152,249 @@ void Map::addThing(const ThingPtr& thing, const Position& pos, int stackPos) if(mustAdd) m_staticTexts.push_back(staticText); - } - else { + } else { tile->addThing(thing, stackPos); } - thing->start(); - thing->setPos(pos); + thing->startAnimation(); + thing->setPosition(pos); + + notificateTileUpdateToMapViews(pos); } ThingPtr Map::getThing(const Position& pos, int stackPos) { - if(const TilePtr& tile = m_tiles[pos]) + if(TilePtr tile = getTile(pos)) return tile->getThing(stackPos); return nullptr; } -void Map::removeThingByPos(const Position& pos, int stackPos) -{ - if(TilePtr& tile = m_tiles[pos]) - tile->removeThingByStackpos(stackPos); -} - -void Map::removeThing(const ThingPtr& thing) +bool Map::removeThing(const ThingPtr& thing) { if(!thing) - return; + return false; - if(MissilePtr shot = thing->asMissile()) { - auto it = std::find(m_missilesAtFloor[shot->getPos().z].begin(), m_missilesAtFloor[shot->getPos().z].end(), shot); - if(it != m_missilesAtFloor[shot->getPos().z].end()) { - m_missilesAtFloor[shot->getPos().z].erase(it); + if(MissilePtr missile = thing->asMissile()) { + auto it = std::find(m_floorMissiles[missile->getPosition().z].begin(), m_floorMissiles[missile->getPosition().z].end(), missile); + if(it != m_floorMissiles[missile->getPosition().z].end()) { + m_floorMissiles[missile->getPosition().z].erase(it); + return true; } - return; - } - else if(AnimatedTextPtr animatedText = thing->asAnimatedText()) { + } else if(AnimatedTextPtr animatedText = thing->asAnimatedText()) { auto it = std::find(m_animatedTexts.begin(), m_animatedTexts.end(), animatedText); - if(it != m_animatedTexts.end()) + if(it != m_animatedTexts.end()) { m_animatedTexts.erase(it); - return; - } - else if(StaticTextPtr staticText = thing->asStaticText()) { + return true; + } + } else if(StaticTextPtr staticText = thing->asStaticText()) { auto it = std::find(m_staticTexts.begin(), m_staticTexts.end(), staticText); - if(it != m_staticTexts.end()) + if(it != m_staticTexts.end()) { m_staticTexts.erase(it); - return; - } + return true; + } + } else if(TilePtr tile = thing->getTile()) + return tile->removeThing(thing); + + notificateTileUpdateToMapViews(thing->getPosition()); - if(TilePtr& tile = m_tiles[thing->getPos()]) - tile->removeThing(thing); + return false; } -TilePtr Map::getTile(const Position& pos) +bool Map::removeThingByPos(const Position& pos, int stackPos) { - if(!pos.isValid()) - return nullptr; + if(TilePtr tile = getTile(pos)) + return removeThing(tile->getThing(stackPos)); + return false; +} - TilePtr& tile = m_tiles[pos]; - if(!tile) - tile = TilePtr(new Tile(pos)); +TilePtr Map::createTile(const Position& pos) +{ + TilePtr tile = TilePtr(new Tile(pos)); + m_tiles[pos] = tile; return tile; } +const TilePtr& Map::getTile(const Position& pos) +{ + auto it = m_tiles.find(pos); + if(it != m_tiles.end()) + return it->second; + static TilePtr nulltile; + return nulltile; +} + +TilePtr Map::getOrCreateTile(const Position& pos) +{ + const TilePtr& tile = getTile(pos); + if(tile) + return tile; + else + return createTile(pos); +} + void Map::cleanTile(const Position& pos) { - if(TilePtr& tile = m_tiles[pos]) + if(TilePtr tile = getTile(pos)) { tile->clean(); + if(tile->canErase()) + m_tiles.erase(m_tiles.find(pos)); + + notificateTileUpdateToMapViews(pos); + } } void Map::addCreature(const CreaturePtr& creature) { - m_creatures[creature->getId()] = creature; + m_knownCreatures[creature->getId()] = creature; } CreaturePtr Map::getCreatureById(uint32 id) { - if(g_game.getLocalPlayer() && (uint32)g_game.getLocalPlayer()->getId() == id) - return g_game.getLocalPlayer(); - return m_creatures[id]; + LocalPlayerPtr localPlayer = g_game.getLocalPlayer(); + if(localPlayer && localPlayer->getId() == id) + return localPlayer; + return m_knownCreatures[id]; } void Map::removeCreatureById(uint32 id) { - m_creatures.erase(id); + if(id == 0) + return; + m_knownCreatures.erase(id); } void Map::setCentralPosition(const Position& centralPosition) { + bool teleported = !m_centralPosition.isInRange(centralPosition, 1,1); m_centralPosition = centralPosition; + + // remove all creatures when teleporting, the server will resend them again + if(teleported) { + for(const auto& pair : m_knownCreatures) { + const CreaturePtr& creature = pair.second; + removeThing(creature); + } + // remove creatures from tiles that we are not aware anymore + } else { + for(const auto& pair : m_knownCreatures) { + const CreaturePtr& creature = pair.second; + if(!isAwareOfPosition(creature->getPosition())) { + removeThing(creature); + } + } + } +} + +std::vector Map::getSpectators(const Position& centerPos, bool multiFloor) +{ + return getSpectatorsInRange(centerPos, multiFloor, (Otc::VISIBLE_X_TILES - 1)/2, (Otc::VISIBLE_Y_TILES - 1)/2); +} + +std::vector Map::getSpectatorsInRange(const Position& centerPos, bool multiFloor, int xRange, int yRange) +{ + return getSpectatorsInRangeEx(centerPos, multiFloor, xRange, xRange, yRange, yRange); +} + +std::vector Map::getSpectatorsInRangeEx(const Position& centerPos, bool multiFloor, int minXRange, int maxXRange, int minYRange, int maxYRange) +{ + int minZRange = 0; + int maxZRange = 0; + std::vector creatures; + + if(multiFloor) { + minZRange = 0; + maxZRange = Otc::MAX_Z; + } + + //TODO: get creatures from other floors corretly + //TODO: delivery creatures in distance order + + for(int iz=-minZRange; iz<=maxZRange; ++iz) { + for(int iy=-minYRange; iy<=maxYRange; ++iy) { + for(int ix=-minXRange; ix<=maxXRange; ++ix) { + TilePtr tile = getTile(centerPos.translated(ix,iy,iz)); + if(!tile) + continue; + + auto tileCreatures = tile->getCreatures(); + creatures.insert(creatures.end(), tileCreatures.rbegin(), tileCreatures.rend()); + } + } + } + + return creatures; } -void Map::setVisibleSize(const Size& visibleSize) +bool Map::isLookPossible(const Position& pos) { - m_visibleSize = visibleSize; + TilePtr tile = getTile(pos); + return tile && tile->isLookPossible(); +} - if(m_visibleSize.width() > MAX_WIDTH || m_visibleSize.height() > MAX_HEIGHT) - m_visibleSize = Size(MAP_VISIBLE_WIDTH, MAP_VISIBLE_HEIGHT); +bool Map::isCovered(const Position& pos, int firstFloor) +{ + // check for tiles on top of the postion + Position tilePos = pos; + while(tilePos.coveredUp() && tilePos.z >= firstFloor) { + TilePtr tile = getTile(tilePos); + // the below tile is covered when the above tile has a full ground + if(tile && tile->isFullGround()) + return true; + } + return false; +} - m_centralOffset = Point(std::ceil(m_visibleSize.width() / 2.0), std::ceil(m_visibleSize.height() / 2.0)); - m_size = m_visibleSize + Size(3, 3); +bool Map::isCompletelyCovered(const Position& pos, int firstFloor) +{ + Position tilePos = pos; + while(tilePos.coveredUp() && tilePos.z >= firstFloor) { + bool covered = true; + // check in 2x2 range tiles that has no transparent pixels + for(int x=0;x<2;++x) { + for(int y=0;y<2;++y) { + const TilePtr& tile = getTile(tilePos.translated(-x, -y)); + if(!tile || !tile->isFullyOpaque()) { + covered = false; + break; + } + } + } + if(covered) + return true; + } + return false; +} - if(m_framebuffer) { - m_framebuffer->resize(Size(m_visibleSize.width() * NUM_TILE_PIXELS, m_visibleSize.height() * NUM_TILE_PIXELS)); +bool Map::isAwareOfPosition(const Position& pos) +{ + if(pos.z < getFirstAwareFloor() || pos.z > getLastAwareFloor()) + return false; + + Position groundedPos = pos; + while(groundedPos.z != m_centralPosition.z) { + if(groundedPos.z > m_centralPosition.z) + groundedPos.coveredUp(); + else + groundedPos.coveredDown(); } + return m_centralPosition.isInRange(groundedPos, Otc::AWARE_X_LEFT_TILES, + Otc::AWARE_X_RIGHT_TILES, + Otc::AWARE_Y_TOP_TILES, + Otc::AWARE_Y_BOTTOM_TILES); +} + +int Map::getFirstAwareFloor() +{ + if(m_centralPosition.z > Otc::SEA_FLOOR) + return m_centralPosition.z-Otc::AWARE_UNDEGROUND_FLOOR_RANGE; + else + return 0; } -Point Map::positionTo2D(const Position& position) +int Map::getLastAwareFloor() { - return Point((m_centralOffset.x - 1 + (position.x - m_centralPosition.x) - (m_centralPosition.z - position.z)) * NUM_TILE_PIXELS, - (m_centralOffset.y - 1 + (position.y - m_centralPosition.y) - (m_centralPosition.z - position.z)) * NUM_TILE_PIXELS); + if(m_centralPosition.z > Otc::SEA_FLOOR) + return std::min(m_centralPosition.z+Otc::AWARE_UNDEGROUND_FLOOR_RANGE, (int)Otc::MAX_Z); + else + return Otc::SEA_FLOOR; } diff --git a/src/otclient/core/map.h b/src/otclient/core/map.h index 1782e555..a403fc0d 100644 --- a/src/otclient/core/map.h +++ b/src/otclient/core/map.h @@ -26,70 +26,65 @@ #include "creature.h" #include "animatedtext.h" #include -#include class Map { public: - enum { - MAP_VISIBLE_WIDTH = 15, - MAP_VISIBLE_HEIGHT = 11, - MAP_SIZE_Z = 8, - MAX_WIDTH = 24, - MAX_HEIGHT = 24, - MAX_Z = 16, - NUM_TILE_PIXELS = 32 - }; + void addMapView(const MapViewPtr& mapView); + void removeMapView(const MapViewPtr& mapView); + void notificateTileUpdateToMapViews(const Position& pos); - Map(); - - void draw(const Rect& rect); + void load(); + void save(); void clean(); - int getFirstVisibleFloor(); - bool isLookPossible(const Position& pos); - bool isCovered(const Position& pos, int firstFloor = 0); - bool isCompletlyCovered(const Position& pos, int firstFloor = 0); - + // thing related void addThing(const ThingPtr& thing, const Position& pos, int stackPos = -1); ThingPtr getThing(const Position& pos, int stackPos); - void removeThingByPos(const Position& pos, int stackPos); - void removeThing(const ThingPtr& thing); - void cleanTile(const Position& pos); - TilePtr getTile(const Position& pos); + bool removeThingByPos(const Position& pos, int stackPos); - void setLight(const Light& light) { m_light = light; } - Light getLight() { return m_light; } - - void setCentralPosition(const Position& centralPosition); - Position getCentralPosition() { return m_centralPosition; } + // tile related + TilePtr createTile(const Position& pos); + const TilePtr& getTile(const Position& pos); + TilePtr getOrCreateTile(const Position& pos); + void cleanTile(const Position& pos); + bool removeThing(const ThingPtr& thing); + // known creature related void addCreature(const CreaturePtr& creature); CreaturePtr getCreatureById(uint32 id); void removeCreatureById(uint32 id); + std::vector getSpectators(const Position& centerPos, bool multiFloor); + std::vector getSpectatorsInRange(const Position& centerPos, bool multiFloor, int xRange, int yRange); + std::vector getSpectatorsInRangeEx(const Position& centerPos, bool multiFloor, int minXRange, int maxXRange, int minYRange, int maxYRange); + + void setLight(const Light& light) { m_light = light; } + void setCentralPosition(const Position& centralPosition); + + bool isLookPossible(const Position& pos); + bool isCovered(const Position& pos, int firstFloor = 0); + bool isCompletelyCovered(const Position& pos, int firstFloor = 0); + bool isAwareOfPosition(const Position& pos); - void setVisibleSize(const Size& visibleSize); - Size getVibibleSize() { return m_visibleSize; } - Point getCentralOffset() { return m_centralOffset; } + Light getLight() { return m_light; } + Position getCentralPosition() { return m_centralPosition; } + int getFirstAwareFloor(); + int getLastAwareFloor(); + const std::vector& getFloorMissiles(int z) { return m_floorMissiles[z]; } - Point positionTo2D(const Position& position); + std::vector getAnimatedTexts() { return m_animatedTexts; } + std::vector getStaticTexts() { return m_staticTexts; } private: std::unordered_map m_tiles; - std::map m_creatures; - std::array, MAX_Z> m_missilesAtFloor; + std::map m_knownCreatures; + std::array, Otc::MAX_Z+1> m_floorMissiles; std::vector m_animatedTexts; std::vector m_staticTexts; + std::vector m_mapViews; Light m_light; Position m_centralPosition; - Size m_size; - Size m_visibleSize; - Point m_centralOffset; - Point m_drawOffset; - - FrameBufferPtr m_framebuffer; - PainterShaderProgramPtr m_shaderProgram; }; extern Map g_map; diff --git a/src/otclient/core/mapview.cpp b/src/otclient/core/mapview.cpp new file mode 100644 index 00000000..0349300c --- /dev/null +++ b/src/otclient/core/mapview.cpp @@ -0,0 +1,530 @@ +/* + * Copyright (c) 2010-2012 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 +#include +#include +#include +#include "creature.h" +#include "map.h" +#include "tile.h" +#include "statictext.h" +#include "animatedtext.h" +#include "missile.h" +#include + +MapView::MapView() +{ + 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->setClearColor(Fw::black); + m_lockedFirstVisibleFloor = -1; + 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"); + assert(m_shaderProgram->link()); +} + +void MapView::draw(const Rect& rect) +{ + // update visible tiles cache when needed + if(m_mustUpdateVisibleTilesCache) + updateVisibleTilesCache(); + + float scaleFactor = m_tileSize/(float)Otc::TILE_PIXELS; + Position cameraPosition = getCameraPosition(); + + int drawFlags = 0; + if(m_viewRange == 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) + drawFlags = Otc::DrawGround | Otc::DrawGroundBorders | Otc::DrawWalls | Otc::DrawItems; + else if(m_viewRange == 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; + + if(m_mustDrawVisibleTilesCache || (drawFlags & Otc::DrawAnimations)) { + m_framebuffer->bind(m_mustCleanFramebuffer); + + 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; + if(tile->getPosition().z != z) + break; + else + ++it; + + tile->draw(transformPositionTo2D(tile->getPosition()), scaleFactor, drawFlags); + } + + if(drawFlags & Otc::DrawMissiles) { + for(const MissilePtr& missile : g_map.getFloorMissiles(z)) { + missile->draw(transformPositionTo2D(missile->getPosition()), scaleFactor, drawFlags & Otc::DrawAnimations); + } + } + } + m_framebuffer->generateMipmaps(); + m_framebuffer->release(); + + m_mustDrawVisibleTilesCache = false; + } + + g_painter.setCustomProgram(m_shaderProgram); + g_painter.setColor(Fw::white); + + 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); + m_framebuffer->draw(rect, srcRect); + + 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); + + // avoid drawing texts on map in far zoom outs + if(m_viewRange == NEAR_VIEW) { + for(const CreaturePtr& creature : m_cachedFloorVisibleCreatures) { + Position pos = creature->getPosition(); + + Point p = transformPositionTo2D(pos) - drawOffset; + p += (creature->getDrawOffset() + Point(8, -10)) * scaleFactor; + 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 + if(pos.z != getCameraPosition().z && !staticText->isYell()) + continue; + + Point p = transformPositionTo2D(pos) - drawOffset; + p.x = p.x * horizontalStretchFactor; + p.y = p.y * verticalStretchFactor; + p += rect.topLeft(); + staticText->draw(p, rect); + } + + 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; + + // dont draw animated texts from covered tiles + if(pos.z != cameraPosition.z && g_map.isCovered(pos, m_cachedFirstVisibleFloor)) + continue; + + Point p = transformPositionTo2D(pos) - drawOffset; + p.x = p.x * horizontalStretchFactor; + p.y = p.y * verticalStretchFactor; + p += rect.topLeft(); + animatedText->draw(p, rect); + } + } else { + // draw a cross in the center instead of our creature + Rect vRect(0, 0, 2, 10); + Rect hRect(0, 0, 10, 2); + vRect.moveCenter(rect.center()); + hRect.moveCenter(rect.center()); + g_painter.setColor(Fw::white); + g_painter.drawFilledRect(vRect); + g_painter.drawFilledRect(hRect); + } +} + +void MapView::updateVisibleTilesCache(int start) +{ + if(start == 0) { + if(m_updateTilesCacheEvent) { + m_updateTilesCacheEvent->cancel(); + m_updateTilesCacheEvent = nullptr; + } + + m_cachedFirstVisibleFloor = getFirstVisibleFloor(); + m_cachedLastVisibleFloor = getLastVisibleFloor(); + + if(m_cachedLastVisibleFloor < m_cachedFirstVisibleFloor) + m_cachedLastVisibleFloor = m_cachedFirstVisibleFloor; + + m_cachedFloorVisibleCreatures.clear(); + m_cachedVisibleTiles.clear(); + + m_mustCleanFramebuffer = true; + m_mustDrawVisibleTilesCache = true; + m_mustUpdateVisibleTilesCache = false; + } else + m_mustCleanFramebuffer = false; + + // there is no tile to render on invalid positions + Position cameraPosition = getCameraPosition(); + if(!cameraPosition.isValid()) + return; + + int count = 0; + bool stop = false; + + // clear current visible tiles cache + m_cachedVisibleTiles.clear(); + m_mustDrawVisibleTilesCache = true; + + // 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) { + 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) { + // only start really looking tiles in the desired start + if(count < start) { + count++; + continue; + } + + // avoid rendering too much tiles at once on far views + if(count - start + 1 > MAX_TILE_UPDATES && m_viewRange >= HUGE_VIEW) { + 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 + if(g_map.isCompletelyCovered(tilePos, m_cachedFirstVisibleFloor)) + continue; + m_cachedVisibleTiles.push_back(tile); + } + count++; + } + } + } else { + static std::vector points; + points.clear(); + assert(m_drawDimension.width() % 2 == 0 && m_drawDimension.height() % 2 == 0); + Point quadTopLeft(m_drawDimension.width()/2 - 1, m_drawDimension.height()/2 - 1); + for(int step = 1; !(quadTopLeft.x < 0 && quadTopLeft.y < 0) && !stop; ++step) { + int quadWidth = std::min(2*step, m_drawDimension.width()); + int quadHeight = std::min(2*step, m_drawDimension.height()); + int fillWidth = (quadTopLeft.x >= 0) ? quadWidth-1 : quadWidth; + int fillHeight = (quadTopLeft.x >= 0) ? quadHeight-1 : quadHeight; + if(quadTopLeft.y >= 0) { + for(int qx=0;qx MAX_TILE_UPDATES) { + stop = true; + break; + } + points.push_back(Point(std::max(quadTopLeft.x, 0) + qx, quadTopLeft.y)); + count++; + } + } + if(quadTopLeft.x >= 0) { + for(int qy=0;qy MAX_TILE_UPDATES) { + stop = true; + break; + } + points.push_back(Point(quadTopLeft.x + quadWidth-1, std::max(quadTopLeft.y, 0) + qy)); + count++; + } + } + if(quadTopLeft.y >= 0) { + for(int qx=0;qx MAX_TILE_UPDATES) { + stop = true; + break; + } + points.push_back(Point(std::max(quadTopLeft.x, 0) + quadWidth-qx-1, quadTopLeft.y + quadHeight-1)); + count++; + } + } + if(quadTopLeft.x >= 0) { + for(int qy=0;qy MAX_TILE_UPDATES) { + stop = true; + break; + } + points.push_back(Point(quadTopLeft.x, std::max(quadTopLeft.y, 0) + quadHeight-qy-1)); + count++; + } + } + quadTopLeft -= Point(1,1); + } + + for(const Point& point : points) { + Position tilePos = cameraPosition.translated(point.x - m_virtualCenterOffset.x, point.y - 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; + m_cachedVisibleTiles.push_back(tile); + } + } + } + } + + if(stop) { + // schedule next update continuation + m_updateTilesCacheEvent = g_dispatcher.addEvent(std::bind(&MapView::updateVisibleTilesCache, asMapView(), count)); + } + if(start == 0) + m_cachedFloorVisibleCreatures = g_map.getSpectators(cameraPosition, false); +} + +void MapView::onTileUpdate(const Position& pos) +{ + //if(m_viewRange == NEAR_VIEW) + requestVisibleTilesCacheUpdate(); +} + +void MapView::lockFirstVisibleFloor(int firstVisibleFloor) +{ + m_lockedFirstVisibleFloor = firstVisibleFloor; + requestVisibleTilesCacheUpdate(); +} + +void MapView::unlockFirstVisibleFloor() +{ + m_lockedFirstVisibleFloor = -1; + requestVisibleTilesCacheUpdate(); +} + +void MapView::followCreature(const CreaturePtr& creature) +{ + m_followingCreature = creature; + m_customCameraPosition = Position(); + requestVisibleTilesCacheUpdate(); +} + +void MapView::setCameraPosition(const Position& pos) +{ + m_customCameraPosition = pos; + m_followingCreature = nullptr; + 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.width() <= 3 || visibleDimension.height() <= 3) { + logTraceError("reach max zoom in"); + return; + } + + int possiblesTileSizes[] = {32,16,8,4,2,1}; + int tileSize = 0; + Size drawDimension = visibleDimension + Size(3,3); + for(int candidateTileSize : possiblesTileSizes) { + Size candidateDrawSize = drawDimension * candidateTileSize; + + // found a valid size + if(candidateDrawSize <= m_framebuffer->getSize()) { + tileSize = candidateTileSize; + break; + } + } + + if(tileSize == 0) { + logTraceError("reached max zoom out"); + return; + } + + Point virtualCenterOffset = (drawDimension/2 - Size(1,1)).toPoint(); + + 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; + + // draw actually more than what is needed to avoid massive recalculations on far views + if(viewRange >= FAR_VIEW) { + Size oldDimension = drawDimension; + drawDimension = (m_framebuffer->getSize() / tileSize); + virtualCenterOffset += (drawDimension - oldDimension).toPoint() / 2; + } + + bool mustUpdate = (m_drawDimension != drawDimension || + m_viewRange != viewRange || + m_virtualCenterOffset != virtualCenterOffset || + m_tileSize != tileSize); + + m_visibleDimension = visibleDimension; + m_drawDimension = drawDimension; + m_tileSize = tileSize; + m_viewRange = viewRange; + m_virtualCenterOffset = virtualCenterOffset; + + if(mustUpdate) + requestVisibleTilesCacheUpdate(); +} + +int MapView::getFirstVisibleFloor() +{ + // return forced first visible floor + if(m_lockedFirstVisibleFloor != -1) + return m_lockedFirstVisibleFloor; + + Position cameraPosition = getCameraPosition(); + + // avoid rendering multifloors in far views + if(m_viewRange >= FAR_VIEW) + return cameraPosition.z; + + // 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; + } + } + } + } + } + + return firstFloor; +} + +int MapView::getLastVisibleFloor() +{ + Position cameraPosition = getCameraPosition(); + + // avoid rendering multifloors in far views + if(m_viewRange >= FAR_VIEW) + return cameraPosition.z; + + // view only underground floors when below sea level + if(cameraPosition.z > Otc::SEA_FLOOR) + return cameraPosition.z + Otc::AWARE_UNDEGROUND_FLOOR_RANGE; + else + return Otc::SEA_FLOOR; +} + +Position MapView::getCameraPosition() +{ + if(m_followingCreature) + return m_followingCreature->getPosition(); + return m_customCameraPosition; +} + +Point MapView::transformPositionTo2D(const Position& position) +{ + Position cameraPosition = getCameraPosition(); + return Point((m_virtualCenterOffset.x + (position.x - cameraPosition.x) - (cameraPosition.z - position.z)) * m_tileSize, + (m_virtualCenterOffset.y + (position.y - cameraPosition.y) - (cameraPosition.z - position.z)) * m_tileSize); +} diff --git a/src/otclient/core/mapview.h b/src/otclient/core/mapview.h new file mode 100644 index 00000000..982490b9 --- /dev/null +++ b/src/otclient/core/mapview.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2010-2012 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. + */ + +#ifndef MAPVIEW_H +#define MAPVIEW_H + +#include "declarations.h" +#include +#include +#include + +class MapView : public LuaObject +{ + enum { + // 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, + FAR_VIEW_AREA = 128*128, + MAX_TILE_UPDATES = NEAR_VIEW_AREA*7 + }; + + enum ViewRange { + NEAR_VIEW, + MID_VIEW, + FAR_VIEW, + HUGE_VIEW + }; + +public: + MapView(); + void draw(const Rect& rect); + +private: + void updateVisibleTilesCache(int start = 0); + void requestVisibleTilesCacheUpdate() { m_mustUpdateVisibleTilesCache = true; } + +protected: + void onTileUpdate(const Position& pos); + + friend class Map; + +public: + void lockFirstVisibleFloor(int firstVisibleFloor); + void unlockFirstVisibleFloor(); + void followCreature(const CreaturePtr& creature); + + void setCameraPosition(const Position& pos); + 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 getFirstVisibleFloor(); + int getLastVisibleFloor(); + Position getCameraPosition(); + Size getVisibleDimension() { return m_visibleDimension; } + Size getVisibleSize() { return m_visibleDimension * m_tileSize; } + CreaturePtr getFollowingCreature() { return m_followingCreature; } + + bool getViewRange() { return m_viewRange; } + bool isAnimated() { return m_animated; } + + Point transformPositionTo2D(const Position& position); + + MapViewPtr asMapView() { return std::static_pointer_cast(shared_from_this()); } + +private: + int m_drawFlags; + int m_lockedFirstVisibleFloor; + int m_cachedFirstVisibleFloor; + int m_cachedLastVisibleFloor; + int m_tileSize; + Size m_drawDimension; + Size m_visibleDimension; + Point m_virtualCenterOffset; + Position m_customCameraPosition; + Position m_framebufferCenterPosition; + Boolean m_mustUpdateVisibleTilesCache; + Boolean m_mustDrawVisibleTilesCache; + Boolean m_mustCleanFramebuffer; + Boolean m_animated; + std::vector m_cachedVisibleTiles; + std::vector m_cachedFloorVisibleCreatures; + EventPtr m_updateTilesCacheEvent; + CreaturePtr m_followingCreature; + FrameBufferPtr m_framebuffer; + PainterShaderProgramPtr m_shaderProgram; + ViewRange m_viewRange; +}; + +#endif diff --git a/src/otclient/core/missile.cpp b/src/otclient/core/missile.cpp index d765050f..8b09ac08 100644 --- a/src/otclient/core/missile.cpp +++ b/src/otclient/core/missile.cpp @@ -27,73 +27,62 @@ #include #include -Missile::Missile() : Thing() +void Missile::draw(const Point& dest, float scaleFactor, bool animate) { - m_startTicks = 0; -} + if(m_id == 0 || !animate) + return; -void Missile::draw(const Point& p, const Rect&) -{ - float time = (g_clock.ticks() - m_startTicks) / m_duration; - internalDraw(p + Point(m_posDelta.x * time, m_posDelta.y * time), 0); + int xPattern = 0, yPattern = 0; + if(m_direction == Otc::NorthWest) { + xPattern = 0; + yPattern = 0; + } else if(m_direction == Otc::North) { + xPattern = 1; + yPattern = 0; + } else if(m_direction == Otc::NorthEast) { + xPattern = 2; + yPattern = 0; + } else if(m_direction == Otc::East) { + xPattern = 2; + yPattern = 1; + } else if(m_direction == Otc::SouthEast) { + xPattern = 2; + yPattern = 2; + } else if(m_direction == Otc::South) { + xPattern = 1; + yPattern = 2; + } else if(m_direction == Otc::SouthWest) { + xPattern = 0; + yPattern = 2; + } else if(m_direction == Otc::West) { + xPattern = 0; + yPattern = 1; + } else { + xPattern = 1; + yPattern = 1; + } + + float fraction = m_animationTimer.ticksElapsed() / m_duration; + internalDraw(dest + m_delta * fraction * scaleFactor, scaleFactor, xPattern, yPattern, 0, 0); } void Missile::setPath(const Position& fromPosition, const Position& toPosition) { - Otc::Direction direction = fromPosition.getDirectionFromPosition(toPosition); - - if(direction == Otc::NorthWest) { - m_xPattern = 0; - m_yPattern = 0; - } - else if(direction == Otc::North) { - m_xPattern = 1; - m_yPattern = 0; - } - else if(direction == Otc::NorthEast) { - m_xPattern = 2; - m_yPattern = 0; - } - else if(direction == Otc::East) { - m_xPattern = 2; - m_yPattern = 1; - } - else if(direction == Otc::SouthEast) { - m_xPattern = 2; - m_yPattern = 2; - } - else if(direction == Otc::South) { - m_xPattern = 1; - m_yPattern = 2; - } - else if(direction == Otc::SouthWest) { - m_xPattern = 0; - m_yPattern = 2; - } - else if(direction == Otc::West) { - m_xPattern = 0; - m_yPattern = 1; - } - else { - m_xPattern = 1; - m_yPattern = 1; - } + m_direction = fromPosition.getDirectionFromPosition(toPosition); - m_pos = fromPosition; - m_posDelta = toPosition - fromPosition; - m_startTicks = g_clock.ticks(); - m_duration = 150 * std::sqrt(Point(m_posDelta.x, m_posDelta.y).length()); - m_posDelta.x *= Map::NUM_TILE_PIXELS; - m_posDelta.y *= Map::NUM_TILE_PIXELS; + m_position = fromPosition; + m_delta = Point(toPosition.x - fromPosition.x, toPosition.y - fromPosition.y); + m_duration = 150 * std::sqrt(m_delta.length()); + m_delta *= Otc::TILE_PIXELS; + m_animationTimer.restart(); // schedule removal auto self = asMissile(); - g_dispatcher.scheduleEvent([self]() { - g_map.removeThing(self); - }, m_duration); + g_dispatcher.scheduleEvent([self]() { g_map.removeThing(self); }, m_duration); } -ThingType *Missile::getType() +void Missile::setId(uint32 id) { - return g_thingsType.getThingType(m_id, ThingsType::Missile); + m_id = id; + m_type = g_thingsType.getThingType(m_id, ThingsType::Missile); } diff --git a/src/otclient/core/missile.h b/src/otclient/core/missile.h index 331b3cc3..4b3a48fc 100644 --- a/src/otclient/core/missile.h +++ b/src/otclient/core/missile.h @@ -24,6 +24,7 @@ #define SHOT_H #include +#include #include "thing.h" class Missile : public Thing @@ -33,22 +34,23 @@ class Missile : public Thing }; public: - Missile(); - - void draw(const Point& p, const Rect&); + void draw(const Point& dest, float scaleFactor, bool animate); void updateAnimation(); + void setId(uint32 id); void setPath(const Position& fromPosition, const Position& toPosition); - ThingType *getType(); + uint32 getId() { return m_id; } MissilePtr asMissile() { return std::static_pointer_cast(shared_from_this()); } private: - ticks_t m_startTicks; - Position m_posDelta; + Timer m_animationTimer; + Point m_delta; float m_duration; + uint16 m_id; + Otc::Direction m_direction; }; #endif diff --git a/src/otclient/core/spritemanager.cpp b/src/otclient/core/spritemanager.cpp index 7c3ecd1a..5716375f 100644 --- a/src/otclient/core/spritemanager.cpp +++ b/src/otclient/core/spritemanager.cpp @@ -22,6 +22,7 @@ #include "spritemanager.h" #include +#include #include SpriteManager g_sprites; @@ -54,6 +55,21 @@ void SpriteManager::unload() m_signature = 0; } +void SpriteManager::preloadSprites() +{ + // preload every 100 sprites, periodically + const int burst = 50; + const int interval = 10; + auto preload = [this](int start) { + for(int i=start;i pixels(4096); int writePos = 0; int read = 0; @@ -116,7 +132,10 @@ TexturePtr SpriteManager::loadSpriteTexture(int id) writePos += 4; } - return TexturePtr(new Texture(32, 32, 4, pixels)); + TexturePtr spriteTex(new Texture(32, 32, 4, &pixels[0])); + spriteTex->setSmooth(true); + spriteTex->generateBilinearMipmaps(pixels); + return spriteTex; } TexturePtr SpriteManager::getSpriteTexture(int id) diff --git a/src/otclient/core/spritemanager.h b/src/otclient/core/spritemanager.h index 130254de..63aca0a6 100644 --- a/src/otclient/core/spritemanager.h +++ b/src/otclient/core/spritemanager.h @@ -33,6 +33,7 @@ public: bool load(const std::string& file); void unload(); + void preloadSprites(); uint32 getSignature() { return m_signature; } int getSpritesCount() { return m_spritesCount; } @@ -45,7 +46,7 @@ private: Boolean m_loaded; uint32 m_signature; - uint16 m_spritesCount; + int m_spritesCount; std::stringstream m_fin; std::vector m_sprites; TexturePtr m_transparentSprite; diff --git a/src/otclient/core/statictext.cpp b/src/otclient/core/statictext.cpp index bb5f6255..baccf05f 100644 --- a/src/otclient/core/statictext.cpp +++ b/src/otclient/core/statictext.cpp @@ -31,17 +31,22 @@ StaticText::StaticText() m_font = g_fonts.getFont("verdana-11px-rounded"); } -void StaticText::draw(const Point& p, const Rect& visibleRect) +void StaticText::draw(const Point& dest, const Rect& parentRect) { - Rect rect = Rect(p - Point(m_textSize.width() / 2, m_textSize.height()) + Point(20, 5), m_textSize); + Rect rect = Rect(dest - Point(m_textSize.width() / 2, m_textSize.height()) + Point(20, 5), m_textSize); Rect boundRect = rect; - boundRect.bind(visibleRect); - if((boundRect.center() - rect.center()).length() < visibleRect.width() / 15) + boundRect.bind(parentRect); + + // draw only if the real center is not too far from the parent center, or its a yell + if((boundRect.center() - rect.center()).length() < parentRect.width() / 15 || isYell()) { + //TODO: cache into a framebuffer m_font->renderText(m_text, boundRect, Fw::AlignCenter, m_color); + } } -bool StaticText::addMessage(const std::string& name, const std::string& type, const std::string& message) +bool StaticText::addMessage(const std::string& name, Otc::SpeakType type, const std::string& message) { + //TODO: this could be moved to lua // First message if(m_messages.size() == 0) { m_name = name; @@ -59,7 +64,7 @@ bool StaticText::addMessage(const std::string& name, const std::string& type, co auto self = asStaticText(); g_dispatcher.scheduleEvent([self]() { self->removeMessage(); - }, std::max(DURATION_PER_CHARACTER * message.length(), MIN_DURATION)); + }, std::max(Otc::STATIC_DURATION_PER_CHARACTER * message.length(), Otc::MIN_STATIC_TEXT_DURATION)); return true; } @@ -72,40 +77,34 @@ void StaticText::removeMessage() // schedule removal auto self = asStaticText(); g_dispatcher.addEvent([self]() { g_map.removeThing(self); }); - } - else + } else compose(); } void StaticText::compose() { + //TODO: this could be moved to lua std::string text; - text.clear(); - if(m_messageType == "say") { + if(m_messageType == Otc::SpeakSay) { text += m_name; text += " says:\n"; m_color = Color(239, 239, 0); - } - else if(m_messageType == "whisper") { + } else if(m_messageType == Otc::SpeakWhisper) { text += m_name; text += " whispers:\n"; m_color = Color(239, 239, 0); - } - else if(m_messageType == "yell") { + } else if(m_messageType == Otc::SpeakYell) { text += m_name; text += " yells:\n"; m_color = Color(239, 239, 0); - } - else if(m_messageType == "monsterSay" || m_messageType == "monsterYell") { + } else if(m_messageType == Otc::SpeakMonsterSay || m_messageType == Otc::SpeakMonsterYell) { m_color = Color(254, 101, 0); - } - else if(m_messageType == "npcToPlayer") { + } else if(m_messageType == Otc::SpeakPrivateNpcToPlayer) { text += m_name; text += " says:\n"; m_color = Color(95, 247, 247); - } - else { + } else { logWarning("unknown speak type: ", m_messageType); } @@ -116,6 +115,6 @@ void StaticText::compose() text += "\n"; } - m_text = m_font->wrapText(text, 200); + m_text = m_font->wrapText(text, Otc::MAX_STATIC_TEXT_WIDTH); m_textSize = m_font->calculateTextRectSize(m_text); } diff --git a/src/otclient/core/statictext.h b/src/otclient/core/statictext.h index 8a753b3c..626af781 100644 --- a/src/otclient/core/statictext.h +++ b/src/otclient/core/statictext.h @@ -29,20 +29,17 @@ class StaticText : public Thing { public: - enum { - DURATION_PER_CHARACTER = 75, - MIN_DURATION = 3000 - }; - StaticText(); - void draw(const Point& p, const Rect& visibleRect); + void draw(const Point& dest, const Rect& parentRect); std::string getName() { return m_name; } - std::string getMessageType() { return m_messageType; } + Otc::SpeakType getMessageType() { return m_messageType; } std::string getFirstMessage() { return m_messages[0]; } - bool addMessage(const std::string& name, const std::string& type, const std::string& message); + bool isYell() { return m_messageType == Otc::SpeakYell || m_messageType == Otc::SpeakMonsterYell; } + + bool addMessage(const std::string& name, Otc::SpeakType type, const std::string& message); void removeMessage(); StaticTextPtr asStaticText() { return std::static_pointer_cast(shared_from_this()); } @@ -52,9 +49,10 @@ private: FontPtr m_font; Size m_textSize; + Boolean m_yell; std::vector m_messages; std::string m_name, m_text; - std::string m_messageType; + Otc::SpeakType m_messageType; Color m_color; }; diff --git a/src/otclient/core/thing.cpp b/src/otclient/core/thing.cpp index a5908c6f..c886806d 100644 --- a/src/otclient/core/thing.cpp +++ b/src/otclient/core/thing.cpp @@ -24,59 +24,59 @@ #include "spritemanager.h" #include "thingstype.h" #include +#include "map.h" +#include "tile.h" Thing::Thing() { - m_id = 0; - m_xPattern = 0; - m_yPattern = 0; - m_zPattern = 0; - m_animation = 0; - m_type = getType(); -} - -void Thing::internalDraw(const Point& p, int layer) -{ - for(int h = 0; h < m_type->dimensions[ThingType::Height]; h++) { - for(int w = 0; w < m_type->dimensions[ThingType::Width]; w++) { - int spriteId = m_type->getSpriteId(w, h, layer, m_xPattern, m_yPattern, m_zPattern, m_animation); - if(!spriteId) - continue; - - TexturePtr spriteTex = g_sprites.getSpriteTexture(spriteId); - - Rect drawRect((p.x - w*32) - m_type->parameters[ThingType::DisplacementX], - (p.y - h*32) - m_type->parameters[ThingType::DisplacementY], - 32, 32); - g_painter.drawTexturedRect(drawRect, spriteTex); - } - } -} - -void Thing::setId(uint32 id) -{ - m_id = id; - m_type = getType(); + m_type = g_thingsType.getEmptyThingType(); } int Thing::getStackPriority() { - if(m_type->properties[ThingType::IsGround]) + if(isGround()) return 0; - else if(m_type->properties[ThingType::IsGroundBorder]) + else if(isGroundBorder()) return 1; - else if(m_type->properties[ThingType::IsOnBottom]) + else if(isOnBottom()) return 2; - else if(m_type->properties[ThingType::IsOnTop]) + else if(isOnTop()) return 3; else if(asCreature()) return 4; - return 5; + else // common items + return 5; } -ThingType *Thing::getType() +const TilePtr& Thing::getTile() { - return g_thingsType.getEmptyThingType(); + return g_map.getTile(m_position); } +int Thing::getStackpos() +{ + const TilePtr& tile = getTile(); + if(tile) + return tile->getThingStackpos(asThing()); + return -1; +} +void Thing::internalDraw(const Point& dest, float scaleFactor, int w, int h, int xPattern, int yPattern, int zPattern, int layer, int animationPhase) +{ + int scaledSize = Otc::TILE_PIXELS * scaleFactor; + + int spriteId = getSpriteId(w, h, layer, xPattern, yPattern, zPattern, animationPhase); + if(spriteId) { + Rect drawRect(dest - getDisplacement()*scaleFactor, Size(scaledSize, scaledSize)); + g_painter.setColor(Fw::white); + g_painter.drawTexturedRect(drawRect, g_sprites.getSpriteTexture(spriteId)); + } +} + +void Thing::internalDraw(const Point& dest, float scaleFactor, int xPattern, int yPattern, int zPattern, int animationPhase) +{ + for(int l = 0; l < getLayers(); ++l) + for(int w = 0; w < getDimensionWidth(); ++w) + for(int h = 0; h < getDimensionHeight(); ++h) + internalDraw(dest - Point(w,h)*Otc::TILE_PIXELS*scaleFactor, scaleFactor, w, h, xPattern, yPattern, zPattern, l, animationPhase); +} diff --git a/src/otclient/core/thing.h b/src/otclient/core/thing.h index 6c83e6d3..ffc0a540 100644 --- a/src/otclient/core/thing.h +++ b/src/otclient/core/thing.h @@ -24,7 +24,7 @@ #define THING_H #include "declarations.h" -#include "thingtype.h" +#include "thingstype.h" #include struct Light @@ -39,23 +39,17 @@ public: Thing(); virtual ~Thing() { } - virtual void start() {} + virtual void startAnimation() { } + virtual void draw(const Point& dest, float scaleFactor, bool animate) { } - virtual void draw(const Point& p, const Rect&) = 0; + virtual void setId(uint32 id) { } + void setPosition(const Position& position) { m_position = position; } - void setId(uint32 id); - virtual void setPos(const Position& position) { m_pos = position; } - - uint32 getId() const { return m_id; } - Position getPos() const { return m_pos; } + virtual uint32 getId() { return 0; } + Position getPosition() { return m_position; } int getStackPriority(); - virtual ThingType *getType(); - int getAnimationPhases() { return m_type->dimensions[ThingType::AnimationPhases]; } - int getGroundSpeed() { return m_type->parameters[ThingType::GroundSpeed]; } - - void setXPattern(int xPattern) { m_xPattern = xPattern; } - void setYPattern(int yPattern) { m_yPattern = yPattern; } - void setZPattern(int zPattern) { m_zPattern = zPattern; } + const TilePtr& getTile(); + int getStackpos(); ThingPtr asThing() { return std::static_pointer_cast(shared_from_this()); } virtual ItemPtr asItem() { return nullptr; } @@ -69,29 +63,55 @@ public: virtual AnimatedTextPtr asAnimatedText() { return nullptr; } virtual StaticTextPtr asStaticText() { return nullptr; } + // type related bool isGround() { return m_type->properties[ThingType::IsGround]; } + bool isFullGround() { return m_type->properties[ThingType::IsFullGround]; } + bool isTranslucent() { return m_type->properties[ThingType::IsTranslucent]; } bool isGroundBorder() { return m_type->properties[ThingType::IsGroundBorder]; } bool isOnBottom() { return m_type->properties[ThingType::IsOnBottom]; } bool isOnTop() { return m_type->properties[ThingType::IsOnTop]; } + bool isDontHide() { return m_type->properties[ThingType::DontHide]; } bool isContainer() { return m_type->properties[ThingType::IsContainer]; } bool isForceUse() { return m_type->properties[ThingType::IsForceUse]; } bool isMultiUse() { return m_type->properties[ThingType::IsMultiUse]; } bool isRotateable() { return m_type->properties[ThingType::IsRotateable]; } bool isNotMoveable() { return m_type->properties[ThingType::IsNotMovable]; } + bool isNotWalkable() { return m_type->properties[ThingType::NotWalkable]; } bool isPickupable() { return m_type->properties[ThingType::IsPickupable]; } - bool ignoreLook() { return m_type->properties[ThingType::IgnoreLook]; } + bool isIgnoreLook() { return m_type->properties[ThingType::IgnoreLook]; } + bool isHangable() { return m_type->properties[ThingType::IsHangable]; } + bool isHookSouth() { return m_type->properties[ThingType::HookSouth]; } + bool isHookEast() { return m_type->properties[ThingType::HookEast]; } bool isStackable() { return m_type->properties[ThingType::IsStackable]; } + bool isAnimateAlways() { return m_type->properties[ThingType::AnimateAlways]; } + bool isLyingCorpse() { return m_type->properties[ThingType::IsLyingCorpse]; } + bool blocksProjectile() { return m_type->properties[ThingType::BlockProjectile]; } bool isFluid() { return m_type->properties[ThingType::IsFluid]; } bool isFluidContainer() { return m_type->properties[ThingType::IsFluidContainer]; } + Size getDimension() { return Size(m_type->dimensions[ThingType::Width], m_type->dimensions[ThingType::Height]); } + int getDimensionWidth() { return m_type->dimensions[ThingType::Width]; } + int getDimensionHeight() { return m_type->dimensions[ThingType::Height]; } + Point getDisplacement() { return Point(m_type->parameters[ThingType::DisplacementX], m_type->parameters[ThingType::DisplacementY]); } + int getNumPatternsX() { return m_type->dimensions[ThingType::PatternX]; } + int getNumPatternsY() { return m_type->dimensions[ThingType::PatternY]; } + int getNumPatternsZ() { return m_type->dimensions[ThingType::PatternZ]; } + int getDisplacementX() { return m_type->parameters[ThingType::DisplacementX]; } + int getDisplacementY() { return m_type->parameters[ThingType::DisplacementY]; } + int getLayers() { return m_type->dimensions[ThingType::Layers]; } + 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 getSpriteId(int w = 0, int h = 0, int layer = 0, + int xPattern = 0, int yPattern = 0, int zPattern = 0, + int animation = 0) { return m_type->getSpriteId(w, h, layer, xPattern, yPattern, zPattern, animation); } protected: - void internalDraw(const Point& p, int layer); + void internalDraw(const Point& dest, float scaleFactor, int w, int h, int xPattern, int yPattern, int zPattern, int layer, int animationPhase); + void internalDraw(const Point& dest, float scaleFactor, int xPattern, int yPattern, int zPattern, int animationPhase); - uint32 m_id; - Position m_pos; + Position m_position; ThingType *m_type; - - int m_xPattern, m_yPattern, m_zPattern, m_animation; }; #endif diff --git a/src/otclient/core/thingstype.cpp b/src/otclient/core/thingstype.cpp index 4773a8b6..bc2bbc84 100644 --- a/src/otclient/core/thingstype.cpp +++ b/src/otclient/core/thingstype.cpp @@ -115,11 +115,12 @@ void ThingsType::parseThingType(std::stringstream& fin, ThingType& thingType) ThingType *ThingsType::getThingType(uint16 id, Categories category) { - assert(id != 0); if(category == Item) id -= 100; else if(category == Creature || category == Effect || category == Missile) id -= 1; - assert(id < m_things[category].size()); + + if(id == 0 || id >= m_things[category].size()) + return &m_emptyThingType; return &m_things[category][id]; } diff --git a/src/otclient/core/thingstype.h b/src/otclient/core/thingstype.h index 2c8264db..66203d0a 100644 --- a/src/otclient/core/thingstype.h +++ b/src/otclient/core/thingstype.h @@ -49,6 +49,9 @@ public: uint32 getSignature() { return m_signature; } bool isLoaded() { return m_loaded; } + uint16 getFirstItemId() { return 100; } + uint16 getMaxItemid() { return m_things[Item].size() + 99; } + private: uint32 m_signature; Boolean m_loaded; diff --git a/src/otclient/core/tile.cpp b/src/otclient/core/tile.cpp index c6d38a05..d901aef8 100644 --- a/src/otclient/core/tile.cpp +++ b/src/otclient/core/tile.cpp @@ -33,70 +33,117 @@ Tile::Tile(const Position& position) { m_drawElevation = 0; - m_pos = position; + m_position = position; } -void Tile::draw(const Point& p, const Rect& visibleRect) +void Tile::draw(const Point& dest, float scaleFactor, int drawFlags) { m_drawElevation = 0; + bool animate = drawFlags & Otc::DrawAnimations; // first bottom items - for(const ThingPtr& thing : m_things) { - ThingType *type = thing->getType(); - if(!type->properties[ThingType::IsGround] && !type->properties[ThingType::IsGroundBorder] && !type->properties[ThingType::IsOnBottom]) - break; - thing->draw(p - m_drawElevation, visibleRect); - m_drawElevation += type->parameters[ThingType::Elevation]; - if(m_drawElevation > MAX_DRAW_ELEVATION) - m_drawElevation = MAX_DRAW_ELEVATION; + if(drawFlags & (Otc::DrawGround | Otc::DrawGroundBorders | Otc::DrawOnBottom)) { + for(const ThingPtr& thing : m_things) { + if(!thing->isGround() && !thing->isGroundBorder() && !thing->isOnBottom()) + break; + + if((thing->isGround() && drawFlags & Otc::DrawGround) || + (thing->isGroundBorder() && drawFlags & Otc::DrawGroundBorders) || + (thing->isOnBottom() && drawFlags & Otc::DrawOnBottom)) + thing->draw(dest - m_drawElevation*scaleFactor, scaleFactor, animate); + + m_drawElevation += thing->getElevation(); + if(m_drawElevation > Otc::MAX_ELEVATION) + m_drawElevation = Otc::MAX_ELEVATION; + } } - // now common items - for(auto it = m_things.rbegin(); it != m_things.rend(); ++it) { - const ThingPtr& thing = *it; - ThingType *type = thing->getType(); - if(thing->asCreature() || type->properties[ThingType::IsOnTop] || type->properties[ThingType::IsOnBottom] || type->properties[ThingType::IsGroundBorder] || type->properties[ThingType::IsGround]) - break; - thing->draw(p - m_drawElevation, visibleRect); - m_drawElevation += type->parameters[ThingType::Elevation]; - if(m_drawElevation > MAX_DRAW_ELEVATION) - m_drawElevation = MAX_DRAW_ELEVATION; + int redrawPreviousTopW = 0; + int redrawPreviousTopH = 0; + + if(drawFlags & Otc::DrawItems) { + // now common items in reverse order + for(auto it = m_things.rbegin(); it != m_things.rend(); ++it) { + const ThingPtr& thing = *it; + if(thing->isOnTop() || thing->isOnBottom() || thing->isGroundBorder() || thing->isGround() || thing->asCreature()) + break; + thing->draw(dest - m_drawElevation*scaleFactor, scaleFactor, animate); + + if(thing->isLyingCorpse()) { + redrawPreviousTopW = std::max(thing->getDimensionWidth(), redrawPreviousTopW); + redrawPreviousTopH = std::max(thing->getDimensionHeight(), redrawPreviousTopH); + } + + m_drawElevation += thing->getElevation(); + if(m_drawElevation > Otc::MAX_ELEVATION) + m_drawElevation = Otc::MAX_ELEVATION; + } } - // we can render creatures in 3x3 range - //TODO: this algorithm is slowing down render too much, but it could be cached to improve framerate - //NOTE: looping for 9 tiles is a dirty way to render walking creatures, must change this later - for(int xi = -1; xi <= 1; ++xi) { - for(int yi = -1; yi <= 1; ++yi) { - for(CreaturePtr creature : g_map.getTile(m_pos + Position(xi, yi, 0))->getCreatures()) { - ThingType *type = creature->getType(); - Rect creatureRect(p.x + xi*32 + creature->getWalkOffset().x - type->parameters[ThingType::DisplacementX], p.y + yi*32 + creature->getWalkOffset().y - type->parameters[ThingType::DisplacementY], 32, 32); - Rect thisTileRect(p.x, p.y, 32, 32); - - // only render creatures where bottom right is inside our rect - if(thisTileRect.contains(creatureRect.bottomRight())) { - creature->draw(Point(p.x + xi*32 - m_drawElevation, p.y + yi*32 - m_drawElevation), visibleRect); + // after we render 2x2 lying corpses, we must redraw previous creatures/ontop above them + if(redrawPreviousTopH > 0 || redrawPreviousTopW > 0) { + int topRedrawFlags = drawFlags & (Otc::DrawCreatures | Otc::DrawEffects | Otc::DrawOnTop | Otc::DrawAnimations); + if(topRedrawFlags) { + for(int y=-redrawPreviousTopH;y<=0;++y) { + for(int x=-redrawPreviousTopW;x<=0;++x) { + if(x == 0 && y == 0) + continue; + const TilePtr& tile = g_map.getTile(m_position.translated(x,y)); + if(tile) + tile->draw(dest + Point(x*Otc::TILE_PIXELS, y*Otc::TILE_PIXELS)*scaleFactor, scaleFactor, topRedrawFlags); } } } } + // creatures + if(drawFlags & Otc::DrawCreatures) { + if(animate) { + for(const CreaturePtr& creature : m_walkingCreatures) { + creature->draw(Point(dest.x + ((creature->getPosition().x - m_position.x)*Otc::TILE_PIXELS - m_drawElevation)*scaleFactor, + dest.y + ((creature->getPosition().y - m_position.y)*Otc::TILE_PIXELS - m_drawElevation)*scaleFactor), scaleFactor, animate); + + } + } + + for(auto it = m_things.rbegin(); it != m_things.rend(); ++it) { + CreaturePtr creature = (*it)->asCreature(); + if(creature && (!creature->isWalking() || !animate)) + creature->draw(dest - m_drawElevation*scaleFactor, scaleFactor, animate); + } + } + // effects - for(const EffectPtr& effect : m_effects) - effect->draw(p - m_drawElevation, visibleRect); + if(drawFlags & Otc::DrawEffects) { + for(const EffectPtr& effect : m_effects) + effect->draw(dest - m_drawElevation*scaleFactor, scaleFactor, animate); + } // top items - for(const ThingPtr& thing : m_things) { - ThingType *type = thing->getType(); - if(type->properties[ThingType::IsOnTop]) - thing->draw(p, visibleRect); + if(drawFlags & Otc::DrawOnTop) { + for(const ThingPtr& thing : m_things) { + if(thing->isOnTop()) + thing->draw(dest, scaleFactor, animate); + } } } void Tile::clean() { - m_things.clear(); - m_effects.clear(); + while(!m_things.empty()) + removeThing(m_things.front()); +} + +void Tile::addWalkingCreature(const CreaturePtr& creature) +{ + m_walkingCreatures.push_back(creature); +} + +void Tile::removeWalkingCreature(const CreaturePtr& creature) +{ + auto it = std::find(m_walkingCreatures.begin(), m_walkingCreatures.end(), creature); + if(it != m_walkingCreatures.end()) + m_walkingCreatures.erase(it); } ThingPtr Tile::addThing(const ThingPtr& thing, int stackPos) @@ -124,9 +171,40 @@ ThingPtr Tile::addThing(const ThingPtr& thing, int stackPos) if(stackPos < (int)m_things.size()) oldObject = m_things[stackPos]; m_things.insert(m_things.begin() + stackPos, thing); + return oldObject; } +bool Tile::removeThing(ThingPtr thing) +{ + if(!thing) + return false; + + bool removed = false; + + if(EffectPtr effect = thing->asEffect()) { + auto it = std::find(m_effects.begin(), m_effects.end(), effect); + if(it != m_effects.end()) { + m_effects.erase(it); + removed = true; + } + } else { + auto it = std::find(m_things.begin(), m_things.end(), thing); + if(it != m_things.end()) { + m_things.erase(it); + removed = true; + } + } + + // reset values managed by this tile + if(removed) { + //thing->setDrawOffset(0); + //thing->setStackpos(0); + } + + return removed; +} + ThingPtr Tile::getThing(int stackPos) { if(stackPos >= 0 && stackPos < (int)m_things.size()) @@ -150,33 +228,6 @@ ThingPtr Tile::getTopThing() return m_things[m_things.size() - 1]; } -ThingPtr Tile::removeThingByStackpos(int stackPos) -{ - ThingPtr oldThing; - if(stackPos >= 0 && stackPos < (int)m_things.size()) { - oldThing = m_things[stackPos]; - m_things.erase(m_things.begin() + stackPos); - } - return oldThing; -} - -ThingPtr Tile::removeThing(const ThingPtr& thing) -{ - if(EffectPtr effect = thing->asEffect()) { - auto it = std::find(m_effects.begin(), m_effects.end(), effect); - if(it != m_effects.end()) - m_effects.erase(it); - return thing; - } - ThingPtr oldThing; - auto it = std::find(m_things.begin(), m_things.end(), thing); - if(it != m_things.end()) { - oldThing = *it; - m_things.erase(it); - } - return oldThing; -} - std::vector Tile::getCreatures() { std::vector creatures; @@ -192,8 +243,7 @@ ItemPtr Tile::getGround() ThingPtr firstObject = getThing(0); if(!firstObject) return nullptr; - ThingType *type = firstObject->getType(); - if(type->properties[ThingType::IsGround]) + if(firstObject->isGround()) return firstObject->asItem(); return nullptr; } @@ -213,7 +263,7 @@ ThingPtr Tile::getTopLookThing() for(uint i = 0; i < m_things.size(); ++i) { ThingPtr thing = m_things[i]; - if(!thing->ignoreLook() && (!thing->isGround() && !thing->isGroundBorder() && !thing->isOnBottom() && !thing->isOnTop())) + if(!thing->isIgnoreLook() && (!thing->isGround() && !thing->isGroundBorder() && !thing->isOnBottom() && !thing->isOnTop())) return thing; } @@ -239,7 +289,7 @@ CreaturePtr Tile::getTopCreature() CreaturePtr creature; for(uint i = 0; i < m_things.size(); ++i) { ThingPtr thing = m_things[i]; - if(thing->asLocalPlayer()) // return local player if there aint no other creature. + if(thing->asLocalPlayer()) // return local player if there is no other creature creature = thing->asCreature(); else if(thing->asCreature() && !thing->asLocalPlayer()) return thing->asCreature(); @@ -288,8 +338,7 @@ bool Tile::isWalkable() return false; for(const ThingPtr& thing : m_things) { - ThingType *type = thing->getType(); - if(type->properties[ThingType::NotWalkable]) + if(thing->isNotWalkable()) return false; if(CreaturePtr creature = thing->asCreature()) { @@ -302,11 +351,8 @@ bool Tile::isWalkable() bool Tile::isFullGround() { - ThingPtr ground = getThing(0); - if(!ground) - return false; - ThingType *type = ground->getType(); - if(type->properties[ThingType::IsGround] && type->properties[ThingType::IsFullGround]) + ItemPtr ground = getGround(); + if(ground && ground->isFullGround()) return true; return false; } @@ -314,19 +360,13 @@ bool Tile::isFullGround() bool Tile::isFullyOpaque() { ThingPtr firstObject = getThing(0); - if(firstObject) { - ThingType *type = firstObject->getType(); - if(type->properties[ThingType::IsFullGround]) - return true; - } - return false; + return firstObject && firstObject->isFullGround(); } bool Tile::isLookPossible() { for(const ThingPtr& thing : m_things) { - ThingType *type = thing->getType(); - if(type->properties[ThingType::BlockProjectile]) + if(thing->blocksProjectile()) return false; } return true; @@ -334,14 +374,15 @@ bool Tile::isLookPossible() bool Tile::isClickable() { - bool hasGround = false, hasOnBottom = false, hasIgnoreLook = false; + bool hasGround = false; + bool hasOnBottom = false; + bool hasIgnoreLook = false; for(const ThingPtr& thing : m_things) { - ThingType *type = thing->getType(); - if(type->properties[ThingType::IsGround]) + if(thing->isGround()) hasGround = true; - if(type->properties[ThingType::IsOnBottom]) + if(thing->isOnBottom()) hasOnBottom = true; - if(type->properties[ThingType::IgnoreLook]) + if(thing->isIgnoreLook()) hasIgnoreLook = true; if((hasGround || hasOnBottom) && !hasIgnoreLook) @@ -350,6 +391,27 @@ bool Tile::isClickable() return false; } +bool Tile::isEmpty() +{ + return m_things.size() == 0; +} + +bool Tile::mustHookEast() +{ + for(const ThingPtr& thing : m_things) + if(thing->isHookEast()) + return true; + return false; +} + +bool Tile::mustHookSouth() +{ + for(const ThingPtr& thing : m_things) + if(thing->isHookSouth()) + return true; + return false; +} + bool Tile::hasCreature() { for(const ThingPtr& thing : m_things) @@ -358,7 +420,17 @@ bool Tile::hasCreature() return false; } -bool Tile::isEmpty() +bool Tile::limitsFloorsView() { - return m_things.size() == 0; + // ground and walls limits the view + ThingPtr firstThing = getThing(0); + if(firstThing && !firstThing->isDontHide() && (firstThing->isGround() || firstThing->isOnBottom())) + return true; + return false; } + +bool Tile::canErase() +{ + return m_walkingCreatures.empty() && m_effects.empty() && m_things.empty(); +} + diff --git a/src/otclient/core/tile.h b/src/otclient/core/tile.h index 4aec1107..cc8dddbb 100644 --- a/src/otclient/core/tile.h +++ b/src/otclient/core/tile.h @@ -28,22 +28,22 @@ class Tile : public LuaObject { - enum { - MAX_DRAW_ELEVATION = 24 - }; public: Tile(const Position& position); - void draw(const Point& p, const Rect& visibleRect); + void draw(const Point& dest, float scaleFactor, int drawFlags); + +public: void clean(); + void addWalkingCreature(const CreaturePtr& creature); + void removeWalkingCreature(const CreaturePtr& creature); + ThingPtr addThing(const ThingPtr& thing, int stackPos = -1); + bool removeThing(ThingPtr thing); ThingPtr getThing(int stackPos); int getThingStackpos(const ThingPtr& thing); ThingPtr getTopThing(); - ThingPtr removeThingByStackpos(int stackPos); - ThingPtr removeThing(const ThingPtr& thing); - ThingPtr getTopLookThing(); ThingPtr getTopUseThing(); @@ -51,26 +51,33 @@ public: ThingPtr getTopMoveThing(); ThingPtr getTopMultiUseThing(); - const Position& getPos() { return m_pos; } + const Position& getPosition() { return m_position; } int getDrawElevation() { return m_drawElevation; } std::vector getCreatures(); + const std::vector& getThings() { return m_things; } ItemPtr getGround(); int getGroundSpeed(); + int getThingCount() { return m_things.size() + m_effects.size(); } bool isWalkable(); bool isFullGround(); bool isFullyOpaque(); bool isLookPossible(); - bool hasCreature(); - bool isEmpty(); bool isClickable(); + bool isEmpty(); + bool mustHookSouth(); + bool mustHookEast(); + bool hasCreature(); + bool limitsFloorsView(); + bool canErase(); TilePtr asTile() { return std::static_pointer_cast(shared_from_this()); } private: - std::vector m_effects; // Leave this outside m_things because it has no stackpos. + std::vector m_walkingCreatures; + std::vector m_effects; // leave this outside m_things because it has no stackpos. std::vector m_things; - Position m_pos; - int m_drawElevation; + Position m_position; + uint8 m_drawElevation; }; #endif diff --git a/src/otclient/luafunctions.cpp b/src/otclient/luafunctions.cpp index 3571b58f..41922212 100644 --- a/src/otclient/luafunctions.cpp +++ b/src/otclient/luafunctions.cpp @@ -58,12 +58,12 @@ void OTClient::registerLuaFunctions() g_lua.bindClassStaticFunction("g_sprites", "load", std::bind(&SpriteManager::load, &g_sprites, _1)); g_lua.bindClassStaticFunction("g_sprites", "isLoaded", std::bind(&SpriteManager::isLoaded, &g_sprites)); g_lua.bindClassStaticFunction("g_sprites", "getSignature", std::bind(&SpriteManager::getSignature, &g_sprites)); + g_lua.bindClassStaticFunction("g_sprites", "preloadSprites", std::bind(&SpriteManager::preloadSprites, &g_sprites)); g_lua.registerStaticClass("g_map"); - g_lua.bindClassStaticFunction("g_map", "getFirstVisibleFloor", std::bind(&Map::getFirstVisibleFloor, &g_map)); g_lua.bindClassStaticFunction("g_map", "isLookPossible", std::bind(&Map::isLookPossible, &g_map, _1)); g_lua.bindClassStaticFunction("g_map", "isCovered", std::bind(&Map::isCovered, &g_map, _1, _2)); - g_lua.bindClassStaticFunction("g_map", "isCompletlyCovered", std::bind(&Map::isCompletlyCovered, &g_map, _1, _2)); + g_lua.bindClassStaticFunction("g_map", "isCompletelyCovered", std::bind(&Map::isCompletelyCovered, &g_map, _1, _2)); g_lua.bindClassStaticFunction("g_map", "addThing", std::bind(&Map::addThing, &g_map, _1, _2, _3)); g_lua.bindClassStaticFunction("g_map", "getThing", std::bind(&Map::getThing, &g_map, _1, _2)); g_lua.bindClassStaticFunction("g_map", "removeThingByPos", std::bind(&Map::removeThingByPos, &g_map, _1, _2)); @@ -72,13 +72,9 @@ void OTClient::registerLuaFunctions() g_lua.bindClassStaticFunction("g_map", "getTile", std::bind(&Map::getTile, &g_map, _1)); g_lua.bindClassStaticFunction("g_map", "setCentralPosition", std::bind(&Map::setCentralPosition, &g_map, _1)); g_lua.bindClassStaticFunction("g_map", "getCentralPosition", std::bind(&Map::getCentralPosition, &g_map)); - g_lua.bindClassStaticFunction("g_map", "addCreature", std::bind(&Map::addCreature, &g_map, _1)); g_lua.bindClassStaticFunction("g_map", "getCreatureById", std::bind(&Map::getCreatureById, &g_map, _1)); g_lua.bindClassStaticFunction("g_map", "removeCreatureById", std::bind(&Map::removeCreatureById, &g_map, _1)); - g_lua.bindClassStaticFunction("g_map", "setVisibleSize", std::bind(&Map::setVisibleSize, &g_map, _1)); - g_lua.bindClassStaticFunction("g_map", "getVibibleSize", std::bind(&Map::getVibibleSize, &g_map)); - g_lua.bindClassStaticFunction("g_map", "getCentralOffset", std::bind(&Map::getCentralOffset, &g_map)); - g_lua.bindClassStaticFunction("g_map", "positionTo2D", std::bind(&Map::positionTo2D, &g_map, _1)); + g_lua.bindClassStaticFunction("g_map", "getSpectators", std::bind(&Map::getSpectators, &g_map, _1, _2)); g_lua.bindGlobalFunction("getOufitColor", Outfit::getColor); @@ -91,14 +87,11 @@ void OTClient::registerLuaFunctions() g_lua.registerClass(); g_lua.bindClassMemberFunction("setId", &Thing::setId); - g_lua.bindClassMemberFunction("setPos", &Thing::setPos); + g_lua.bindClassMemberFunction("setPosition", &Thing::setPosition); g_lua.bindClassMemberFunction("getId", &Thing::getId); - g_lua.bindClassMemberFunction("getPos", &Thing::getPos); + g_lua.bindClassMemberFunction("getPosition", &Thing::getPosition); g_lua.bindClassMemberFunction("getStackPriority", &Thing::getStackPriority); g_lua.bindClassMemberFunction("getAnimationPhases", &Thing::getAnimationPhases); - g_lua.bindClassMemberFunction("setXPattern", &Thing::setXPattern); - g_lua.bindClassMemberFunction("setYPattern", &Thing::setYPattern); - g_lua.bindClassMemberFunction("setZPattern", &Thing::setZPattern); g_lua.bindClassMemberFunction("asThing", &Thing::asThing); g_lua.bindClassMemberFunction("asItem", &Thing::asItem); g_lua.bindClassMemberFunction("asCreature", &Thing::asCreature); @@ -118,8 +111,11 @@ void OTClient::registerLuaFunctions() g_lua.bindClassMemberFunction("isRotateable", &Thing::isRotateable); g_lua.bindClassMemberFunction("isNotMoveable", &Thing::isNotMoveable); g_lua.bindClassMemberFunction("isPickupable", &Thing::isPickupable); - g_lua.bindClassMemberFunction("ignoreLook", &Thing::ignoreLook); + g_lua.bindClassMemberFunction("isIgnoreLook", &Thing::isIgnoreLook); g_lua.bindClassMemberFunction("isStackable", &Thing::isStackable); + g_lua.bindClassMemberFunction("isHookSouth", &Thing::isHookSouth); + g_lua.bindClassMemberFunction("isTranslucent", &Thing::isTranslucent); + g_lua.bindClassMemberFunction("isFullGround", &Thing::isFullGround); g_lua.registerClass(); g_lua.bindClassMemberFunction("getName", &Creature::getName); @@ -133,7 +129,7 @@ void OTClient::registerLuaFunctions() g_lua.registerClass(); g_lua.bindClassStaticFunction("create", &Item::create); - g_lua.bindClassMemberFunction("getData", &Item::getData); + g_lua.bindClassMemberFunction("getCount", &Item::getCount); g_lua.registerClass(); g_lua.registerClass(); @@ -141,6 +137,7 @@ void OTClient::registerLuaFunctions() g_lua.registerClass(); g_lua.registerClass(); + g_lua.bindClassMemberFunction("isWalking", &Creature::isWalking); g_lua.registerClass(); g_lua.registerClass(); g_lua.registerClass(); @@ -165,14 +162,13 @@ void OTClient::registerLuaFunctions() g_lua.bindClassMemberFunction("getThing", &Tile::getThing); g_lua.bindClassMemberFunction("getThingStackpos", &Tile::getThingStackpos); g_lua.bindClassMemberFunction("getTopThing", &Tile::getTopThing); - g_lua.bindClassMemberFunction("removeThingByStackpos", &Tile::removeThingByStackpos); g_lua.bindClassMemberFunction("removeThing", &Tile::removeThing); g_lua.bindClassMemberFunction("getTopLookThing", &Tile::getTopLookThing); g_lua.bindClassMemberFunction("getTopUseThing", &Tile::getTopUseThing); g_lua.bindClassMemberFunction("getTopCreature", &Tile::getTopCreature); g_lua.bindClassMemberFunction("getTopMoveThing", &Tile::getTopMoveThing); g_lua.bindClassMemberFunction("getTopMultiUseThing", &Tile::getTopMultiUseThing); - g_lua.bindClassMemberFunction("getPos", &Tile::getPos); + g_lua.bindClassMemberFunction("getPosition", &Tile::getPosition); g_lua.bindClassMemberFunction("getDrawElevation", &Tile::getDrawElevation); g_lua.bindClassMemberFunction("getCreatures", &Tile::getCreatures); g_lua.bindClassMemberFunction("getGround", &Tile::getGround); @@ -194,6 +190,9 @@ void OTClient::registerLuaFunctions() g_lua.bindClassStaticFunction("requestOutfit", std::bind(&Game::requestOutfit, &g_game)); g_lua.bindClassStaticFunction("requestChannels", std::bind(&Game::requestChannels, &g_game)); g_lua.bindClassStaticFunction("joinChannel", std::bind(&Game::joinChannel, &g_game, _1)); + g_lua.bindClassStaticFunction("leaveChannel", std::bind(&Game::leaveChannel, &g_game, _1)); + g_lua.bindClassStaticFunction("closeNpcChannel", std::bind(&Game::closeNpcChannel, &g_game)); + g_lua.bindClassStaticFunction("openPrivateChannel", std::bind(&Game::openPrivateChannel, &g_game, _1)); g_lua.bindClassStaticFunction("setOutfit", std::bind(&Game::setOutfit, &g_game, _1)); g_lua.bindClassStaticFunction("look", std::bind(&Game::look, &g_game, _1)); g_lua.bindClassStaticFunction("open", std::bind(&Game::open, &g_game, _1, _2)); @@ -201,6 +200,7 @@ void OTClient::registerLuaFunctions() g_lua.bindClassStaticFunction("useWith", std::bind(&Game::useWith, &g_game, _1, _2)); g_lua.bindClassStaticFunction("move", std::bind(&Game::move, &g_game, _1, _2, _3)); g_lua.bindClassStaticFunction("useInventoryItem", std::bind(&Game::useInventoryItem, &g_game, _1, _2)); + g_lua.bindClassStaticFunction("turn", std::bind(&Game::turn, &g_game, _1)); g_lua.bindClassStaticFunction("walk", std::bind(&Game::walk, &g_game, _1)); g_lua.bindClassStaticFunction("forceWalk", std::bind(&Game::forceWalk, &g_game, _1)); g_lua.bindClassStaticFunction("attack", std::bind(&Game::attack, &g_game, _1)); @@ -226,9 +226,16 @@ void OTClient::registerLuaFunctions() g_lua.bindClassStaticFunction("getProtocolGame", std::bind(&Game::getProtocolGame, &g_game)); g_lua.registerClass(); - g_lua.bindClassStaticFunction("create", []{ return UIItemPtr(new UIItem); } ); - g_lua.bindClassMemberFunction("getItem", &UIItem::getItem); + g_lua.bindClassStaticFunction("create", []{ return UIItemPtr(new UIItem); }); + g_lua.bindClassMemberFunction("setItemId", &UIItem::setItemId); + g_lua.bindClassMemberFunction("setItemCount", &UIItem::setItemCount); g_lua.bindClassMemberFunction("setItem", &UIItem::setItem); + g_lua.bindClassMemberFunction("setVirtual", &UIItem::setVirtual); + g_lua.bindClassMemberFunction("getItemId", &UIItem::getItemId); + g_lua.bindClassMemberFunction("getItemCount", &UIItem::getItemCount); + g_lua.bindClassMemberFunction("getItem", &UIItem::getItem); + g_lua.bindClassMemberFunction("isVirtual", &UIItem::isVirtual); + g_lua.registerClass(); g_lua.bindClassStaticFunction("create", []{ return UICreaturePtr(new UICreature); } ); @@ -238,6 +245,8 @@ void OTClient::registerLuaFunctions() g_lua.registerClass(); g_lua.bindClassStaticFunction("create", []{ return UIMapPtr(new UIMap); } ); g_lua.bindClassMemberFunction("getTile", &UIMap::getTile); + g_lua.bindClassMemberFunction("zoomIn", &UIMap::zoomIn); + g_lua.bindClassMemberFunction("zoomOut", &UIMap::zoomOut); g_lua.registerClass(); g_lua.bindClassStaticFunction("create", []{ return UIGamePtr(new UIGame); } ); diff --git a/src/otclient/net/protocolcodes.h b/src/otclient/net/protocolcodes.h index 9782b71e..fe1b8fe0 100644 --- a/src/otclient/net/protocolcodes.h +++ b/src/otclient/net/protocolcodes.h @@ -123,7 +123,7 @@ namespace Proto { GameServerTalk = 170, GameServerChannels = 171, GameServerOpenChannel = 172, - GameServerPrivateChannel = 173, + GameServerOpenPrivateChannel = 173, GameServerRuleViolationChannel = 174, // deprecated in last tibia GameServerRuleViolationRemove = 175, // deprecated in last tibia GameServerRuleViolationCancel = 176, // deprecated in last tibia @@ -190,7 +190,7 @@ namespace Proto { ClientGetChannels = 151, ClientJoinChannel = 152, ClientLeaveChannel = 153, - ClientPrivateChannel = 154, + ClientOpenPrivateChannel = 154, ClientCloseNpcChannel = 158, ClientSetTactics = 160, ClientAttack = 161, @@ -238,29 +238,29 @@ namespace Proto { SpeakMonsterYell, // removed - SpeakRVRChannel = 255, - SpeakRVRAnswer, - SpeakRVRContinue, - SpeakChannelRed2 + ServerSpeakRVRChannel = 255, + ServerSpeakRVRAnswer, + ServerSpeakRVRContinue, + ServerSpeakChannelRed2 #elif PROTOCOL==860 - SpeakSay = 1, - SpeakWhisper, - SpeakYell, - SpeakPrivatePlayerToNpc, - SpeakPrivateNpcToPlayer, - SpeakPrivate, - SpeakChannelYellow, - SpeakChannelWhite, - SpeakRVRChannel, - SpeakRVRAnswer, - SpeakRVRContinue, - SpeakBroadcast, - SpeakChannelRed, - SpeakPrivateRed, - SpeakChannelOrange, - SpeakChannelRed2 = 17, - SpeakMonsterSay = 19, - SpeakMonsterYell + ServerSpeakSay = 1, + ServerSpeakWhisper, + ServerSpeakYell, + ServerSpeakPrivatePlayerToNpc, + ServerSpeakPrivateNpcToPlayer, + ServerSpeakPrivate, + ServerSpeakChannelYellow, + ServerSpeakChannelWhite, + ServerSpeakRVRChannel, + ServerSpeakRVRAnswer, + ServerSpeakRVRContinue, + ServerSpeakBroadcast, + ServerSpeakChannelRed, + ServerSpeakPrivateRed, + ServerSpeakChannelOrange, + ServerSpeakChannelRed2 = 17, + ServerSpeakMonsterSay = 19, + ServerSpeakMonsterYell #endif }; @@ -299,48 +299,48 @@ namespace Proto { NpcEndId = 0xffffffff }; - inline std::string translateSpeakType(int type) { + inline Otc::SpeakType translateSpeakTypeFromServer(int type) { switch(type) { - case Proto::SpeakSay: return "say"; - case Proto::SpeakWhisper: return "whisper"; - case Proto::SpeakYell: return "yell"; - case Proto::SpeakMonsterSay: return "monsterSay"; - case Proto::SpeakMonsterYell: return "monsterYell"; - case Proto::SpeakPrivateNpcToPlayer: return "npcToPlayer"; - case Proto::SpeakChannelYellow: return "channelYellow"; - case Proto::SpeakChannelWhite: return "channelWhite"; - case Proto::SpeakChannelRed: return "channelRed"; - case Proto::SpeakChannelRed2: return "channelRed"; - case Proto::SpeakChannelOrange: return "channelOrange"; - case Proto::SpeakPrivate: return "private"; - case Proto::SpeakPrivatePlayerToNpc: return "playerToNpc"; - case Proto::SpeakBroadcast: return "broadcast"; - case Proto::SpeakPrivateRed: return "privateRed"; + case Proto::ServerSpeakSay: return Otc::SpeakSay; + case Proto::ServerSpeakWhisper: return Otc::SpeakWhisper; + case Proto::ServerSpeakYell: return Otc::SpeakYell; + case Proto::ServerSpeakMonsterSay: return Otc::SpeakMonsterSay; + case Proto::ServerSpeakMonsterYell: return Otc::SpeakMonsterYell; + case Proto::ServerSpeakPrivateNpcToPlayer: return Otc::SpeakPrivateNpcToPlayer; + case Proto::ServerSpeakChannelYellow: return Otc::SpeakChannelYellow; + case Proto::ServerSpeakChannelWhite: return Otc::SpeakChannelWhite; + case Proto::ServerSpeakChannelRed: return Otc::SpeakChannelRed; + case Proto::ServerSpeakChannelRed2: return Otc::SpeakChannelRed; + case Proto::ServerSpeakChannelOrange: return Otc::SpeakChannelOrange; + case Proto::ServerSpeakPrivate: return Otc::SpeakPrivate; + case Proto::ServerSpeakPrivatePlayerToNpc: return Otc::SpeakPrivate; + case Proto::ServerSpeakBroadcast: return Otc::SpeakBroadcast; + case Proto::ServerSpeakPrivateRed: return Otc::SpeakPrivateRed; default: logError("unknown protocol speak type ", type); - return "unknown"; + return Otc::SpeakSay; } } - inline int translateSpeakTypeDesc(const std::string& type) { - if(type == "say") return Proto::SpeakSay; - else if(type == "whisper") return Proto::SpeakWhisper; - else if(type == "yell") return Proto::SpeakYell; - else if(type == "monsterSay") return Proto::SpeakMonsterSay; - else if(type == "monsterYell") return Proto::SpeakMonsterYell; - else if(type == "npcToPlayer") return Proto::SpeakPrivateNpcToPlayer; - else if(type == "channelYellow") return Proto::SpeakChannelYellow; - else if(type == "channelWhite") return Proto::SpeakChannelWhite; - else if(type == "channelRed") return Proto::SpeakChannelRed; - else if(type == "channelRed") return Proto::SpeakChannelRed2; - else if(type == "channelOrange") return Proto::SpeakChannelOrange; - else if(type == "private") return Proto::SpeakPrivate; - else if(type == "playerToNpc") return Proto::SpeakPrivatePlayerToNpc; - else if(type == "broadcast") return Proto::SpeakBroadcast; - else if(type == "privateRed") return Proto::SpeakPrivateRed; - else { - logError("unknown protocol speak type desc ", type); - return 0; + inline Proto::ServerSpeakType translateSpeakTypeToServer(int type) { + switch(type) { + case Otc::SpeakSay: return Proto::ServerSpeakSay; + case Otc::SpeakWhisper: return Proto::ServerSpeakWhisper; + case Otc::SpeakYell: return Proto::ServerSpeakYell; + case Otc::SpeakBroadcast: return Proto::ServerSpeakBroadcast; + case Otc::SpeakPrivate: return Proto::ServerSpeakPrivate; + case Otc::SpeakPrivateRed: return Proto::ServerSpeakPrivateRed; + case Otc::SpeakPrivatePlayerToNpc: return Proto::ServerSpeakPrivatePlayerToNpc; + case Otc::SpeakPrivateNpcToPlayer: return Proto::ServerSpeakPrivateNpcToPlayer; + case Otc::SpeakChannelYellow: return Proto::ServerSpeakChannelYellow; + case Otc::SpeakChannelWhite: return Proto::ServerSpeakChannelWhite; + case Otc::SpeakChannelRed: return Proto::ServerSpeakChannelRed; + case Otc::SpeakChannelOrange: return Proto::ServerSpeakChannelOrange; + case Otc::SpeakMonsterSay: return Proto::ServerSpeakMonsterSay; + case Otc::SpeakMonsterYell: return Proto::ServerSpeakMonsterYell; + default: + logError("unknown protocol speak type desc ", type); + return Proto::ServerSpeakSay; } } diff --git a/src/otclient/net/protocolgame.h b/src/otclient/net/protocolgame.h index 38e5f1e5..e97b6d13 100644 --- a/src/otclient/net/protocolgame.h +++ b/src/otclient/net/protocolgame.h @@ -73,11 +73,11 @@ public: void sendTextWindow(uint windowTextId, const std::string& text); void sendHouseWindow(int doorId, uint id, const std::string& text); void sendLookAt(const Position& position, int thingId, int stackpos); - void sendTalk(const std::string& speakTypeDesc, int channelId, const std::string& receiver, const std::string& message); + void sendTalk(Otc::SpeakType speakType, int channelId, const std::string& receiver, const std::string& message); void sendGetChannels(); void sendJoinChannel(int channelId); void sendLeaveChannel(int channelId); - void sendPrivateChannel(const std::string& receiver); + void sendOpenPrivateChannel(const std::string& receiver); void sendCloseNpcChannel(); void sendFightTatics(Otc::FightModes fightMode, Otc::ChaseModes chaseMode, bool safeFight); void sendAttack(uint creatureId); @@ -155,9 +155,9 @@ private: void parseCreatureSpeak(InputMessage& msg); void parseChannelList(InputMessage& msg); void parseOpenChannel(InputMessage& msg); - void parseOpenPrivatePlayerChat(InputMessage& msg); - void parseCreatePrivateChannel(InputMessage& msg); - void parseClosePrivateChannel(InputMessage& msg); + void parseOpenPrivateChannel(InputMessage& msg); + void parseCreateOwnPrivateChannel(InputMessage& msg); + void parseCloseChannel(InputMessage& msg); void parseSafeTradeRequest(InputMessage& msg); void parseSafeTradeClose(InputMessage&); void parseTextMessage(InputMessage& msg); diff --git a/src/otclient/net/protocolgameparse.cpp b/src/otclient/net/protocolgameparse.cpp index aed1c6a3..cc7fa337 100644 --- a/src/otclient/net/protocolgameparse.cpp +++ b/src/otclient/net/protocolgameparse.cpp @@ -195,8 +195,8 @@ void ProtocolGame::parseMessage(InputMessage& msg) case Proto::GameServerOpenChannel: parseOpenChannel(msg); break; - case Proto::GameServerPrivateChannel: - parseOpenPrivatePlayerChat(msg); + case Proto::GameServerOpenPrivateChannel: + parseOpenPrivateChannel(msg); break; case Proto::GameServerRuleViolationChannel: msg.getU16(); @@ -210,10 +210,10 @@ void ProtocolGame::parseMessage(InputMessage& msg) case Proto::GameServerRuleViolationLock: break; case Proto::GameServerOpenOwnChannel: - parseCreatePrivateChannel(msg); + parseCreateOwnPrivateChannel(msg); break; case Proto::GameServerCloseChannel: - parseClosePrivateChannel(msg); + parseCloseChannel(msg); break; case Proto::GameServerMessage: parseTextMessage(msg); @@ -317,7 +317,7 @@ void ProtocolGame::parseMapDescription(InputMessage& msg) { Position pos = parsePosition(msg); g_map.setCentralPosition(pos); - setMapDescription(msg, pos.x - 8, pos.y - 6, pos.z, 18, 14); + setMapDescription(msg, pos.x - Otc::AWARE_X_LEFT_TILES, pos.y - Otc::AWARE_Y_TOP_TILES, pos.z, Otc::AWARE_X_TILES, Otc::AWARE_Y_TILES); } void ProtocolGame::parseMoveNorth(InputMessage& msg) @@ -325,7 +325,7 @@ void ProtocolGame::parseMoveNorth(InputMessage& msg) Position pos = g_map.getCentralPosition(); pos.y--; - setMapDescription(msg, pos.x - 8, pos.y - 6, pos.z, 18, 1); + setMapDescription(msg, pos.x - Otc::AWARE_X_LEFT_TILES, pos.y - Otc::AWARE_Y_TOP_TILES, pos.z, Otc::AWARE_X_TILES, 1); g_map.setCentralPosition(pos); } @@ -334,7 +334,7 @@ void ProtocolGame::parseMoveEast(InputMessage& msg) Position pos = g_map.getCentralPosition(); pos.x++; - setMapDescription(msg, pos.x + 9, pos.y - 6, pos.z, 1, 14); + setMapDescription(msg, pos.x + Otc::AWARE_X_RIGHT_TILES, pos.y - Otc::AWARE_Y_TOP_TILES, pos.z, 1, Otc::AWARE_Y_TILES); g_map.setCentralPosition(pos); } @@ -343,7 +343,7 @@ void ProtocolGame::parseMoveSouth(InputMessage& msg) Position pos = g_map.getCentralPosition(); pos.y++; - setMapDescription(msg, pos.x - 8, pos.y + 7, pos.z, 18, 1); + setMapDescription(msg, pos.x - Otc::AWARE_X_LEFT_TILES, pos.y + Otc::AWARE_Y_BOTTOM_TILES, pos.z, Otc::AWARE_X_TILES, 1); g_map.setCentralPosition(pos); } @@ -352,7 +352,7 @@ void ProtocolGame::parseMoveWest(InputMessage& msg) Position pos = g_map.getCentralPosition(); pos.x--; - setMapDescription(msg, pos.x - 8, pos.y - 6, pos.z, 1, 14); + setMapDescription(msg, pos.x - Otc::AWARE_X_LEFT_TILES, pos.y - Otc::AWARE_Y_TOP_TILES, pos.z, 1, Otc::AWARE_Y_TILES); g_map.setCentralPosition(pos); } @@ -407,7 +407,7 @@ void ProtocolGame::parseCreatureMove(InputMessage& msg) int oldStackpos = msg.getU8(); Position newPos = parsePosition(msg); - ThingPtr thing = g_map.getTile(oldPos)->getThing(oldStackpos); + ThingPtr thing = g_map.getThing(oldPos, oldStackpos); if(!thing) { logTraceError("could not get thing"); return; @@ -566,10 +566,10 @@ void ProtocolGame::parseDistanceMissile(InputMessage& msg) Position toPos = parsePosition(msg); int shotId = msg.getU8(); - MissilePtr shot = MissilePtr(new Missile()); - shot->setId(shotId); - shot->setPath(fromPos, toPos); - g_map.addThing(shot, fromPos); + MissilePtr missile = MissilePtr(new Missile()); + missile->setId(shotId); + missile->setPath(fromPos, toPos); + g_map.addThing(missile, fromPos); } void ProtocolGame::parseCreatureSquare(InputMessage& msg) @@ -720,43 +720,43 @@ void ProtocolGame::parseCreatureSpeak(InputMessage& msg) msg.getU32(); // unkSpeak std::string name = msg.getString(); int level = msg.getU16(); - int type = msg.getU8(); + int serverType = msg.getU8(); int channelId = 0; Position creaturePos; - switch(type) { - case Proto::SpeakSay: - case Proto::SpeakWhisper: - case Proto::SpeakYell: - case Proto::SpeakMonsterSay: - case Proto::SpeakMonsterYell: - case Proto::SpeakPrivateNpcToPlayer: + switch(serverType) { + case Proto::ServerSpeakSay: + case Proto::ServerSpeakWhisper: + case Proto::ServerSpeakYell: + case Proto::ServerSpeakMonsterSay: + case Proto::ServerSpeakMonsterYell: + case Proto::ServerSpeakPrivateNpcToPlayer: creaturePos = parsePosition(msg); break; - case Proto::SpeakChannelYellow: - case Proto::SpeakChannelWhite: - case Proto::SpeakChannelRed: - case Proto::SpeakChannelRed2: - case Proto::SpeakChannelOrange: + case Proto::ServerSpeakChannelYellow: + case Proto::ServerSpeakChannelWhite: + case Proto::ServerSpeakChannelRed: + case Proto::ServerSpeakChannelRed2: + case Proto::ServerSpeakChannelOrange: channelId = msg.getU16(); break; - case Proto::SpeakPrivate: - case Proto::SpeakPrivatePlayerToNpc: - case Proto::SpeakBroadcast: - case Proto::SpeakPrivateRed: + case Proto::ServerSpeakPrivate: + case Proto::ServerSpeakPrivatePlayerToNpc: + case Proto::ServerSpeakBroadcast: + case Proto::ServerSpeakPrivateRed: break; - case Proto::SpeakRVRChannel: + case Proto::ServerSpeakRVRChannel: msg.getU32(); break; default: - logTraceError("unknown speak type ", type); + logTraceError("unknown speak type ", serverType); break; } std::string message = msg.getString(); - std::string typeDesc = Proto::translateSpeakType(type); + Otc::SpeakType type = Proto::translateSpeakTypeFromServer(serverType); - g_game.processCreatureSpeak(name, level, typeDesc, message, channelId, creaturePos); + g_game.processCreatureSpeak(name, level, type, message, channelId, creaturePos); } void ProtocolGame::parseChannelList(InputMessage& msg) @@ -780,20 +780,26 @@ void ProtocolGame::parseOpenChannel(InputMessage& msg) g_lua.callGlobalField("Game", "onOpenChannel", channelId, name); } -void ProtocolGame::parseOpenPrivatePlayerChat(InputMessage& msg) +void ProtocolGame::parseOpenPrivateChannel(InputMessage& msg) { - msg.getString(); // name + std::string name = msg.getString(); + + g_lua.callGlobalField("Game", "onOpenPrivateChannel", name); } -void ProtocolGame::parseCreatePrivateChannel(InputMessage& msg) +void ProtocolGame::parseCreateOwnPrivateChannel(InputMessage& msg) { - msg.getU16(); // channel id - msg.getString(); // channel name + int id = msg.getU16(); // channel id + std::string name = msg.getString(); // channel name + + g_lua.callGlobalField("Game", "onOpenOwnPrivateChannel", id, name); } -void ProtocolGame::parseClosePrivateChannel(InputMessage& msg) +void ProtocolGame::parseCloseChannel(InputMessage& msg) { - msg.getU16(); // channel id + int id = msg.getU16(); // channel id + + g_lua.callGlobalField("Game", "onCloseChannel", id); } void ProtocolGame::parseTextMessage(InputMessage& msg) @@ -819,11 +825,11 @@ void ProtocolGame::parseFloorChangeUp(InputMessage& msg) pos.z--; int skip = 0; - if(pos.z == 7) - for(int i = 5; i >= 0; i--) - setFloorDescription(msg, pos.x - 8, pos.y - 6, i, 18, 14, 8 - i, &skip); - else if(pos.z > 7) - setFloorDescription(msg, pos.x - 8, pos.y - 6, pos.z - 2, 18, 14, 3, &skip); + if(pos.z == Otc::SEA_FLOOR) + for(int i = Otc::SEA_FLOOR - Otc::AWARE_UNDEGROUND_FLOOR_RANGE; i >= 0; i--) + setFloorDescription(msg, pos.x - Otc::AWARE_X_LEFT_TILES, pos.y - Otc::AWARE_Y_TOP_TILES, i, Otc::AWARE_X_TILES, Otc::AWARE_Y_TILES, 8 - i, &skip); + else if(pos.z > Otc::SEA_FLOOR) + setFloorDescription(msg, pos.x - Otc::AWARE_X_LEFT_TILES, pos.y - Otc::AWARE_Y_TOP_TILES, pos.z - Otc::AWARE_UNDEGROUND_FLOOR_RANGE, Otc::AWARE_X_TILES, Otc::AWARE_Y_TILES, 3, &skip); pos.x++; pos.y++; @@ -836,13 +842,13 @@ void ProtocolGame::parseFloorChangeDown(InputMessage& msg) pos.z++; int skip = 0; - if(pos.z == 8) { + if(pos.z == Otc::UNDERGROUND_FLOOR) { int j, i; - for(i = pos.z, j = -1; i < pos.z + 3; ++i, --j) - setFloorDescription(msg, pos.x - 8, pos.y - 6, i, 18, 14, j, &skip); + for(i = pos.z, j = -1; i <= pos.z + Otc::AWARE_UNDEGROUND_FLOOR_RANGE; ++i, --j) + setFloorDescription(msg, pos.x - Otc::AWARE_X_LEFT_TILES, pos.y - Otc::AWARE_Y_TOP_TILES, i, Otc::AWARE_X_TILES, Otc::AWARE_Y_TILES, j, &skip); } - else if(pos.z > 8 && pos.z < 14) - setFloorDescription(msg, pos.x - 8, pos.y - 6, pos.z + 2, 18, 14, -3, &skip); + else if(pos.z > Otc::UNDERGROUND_FLOOR && pos.z < Otc::MAX_Z-1) + setFloorDescription(msg, pos.x - Otc::AWARE_X_LEFT_TILES, pos.y - Otc::AWARE_Y_TOP_TILES, pos.z + Otc::AWARE_UNDEGROUND_FLOOR_RANGE, Otc::AWARE_X_TILES, Otc::AWARE_Y_TILES, -3, &skip); pos.x--; pos.y--; @@ -866,7 +872,7 @@ void ProtocolGame::parseOutfitWindow(InputMessage& msg) } CreaturePtr creature = CreaturePtr(new Creature); - creature->setXPattern(2); + creature->setDirection(Otc::South); creature->setOutfit(outfit); g_lua.callGlobalField("Game", "onOpenOutfitWindow", creature, outfitList); @@ -931,13 +937,13 @@ void ProtocolGame::setMapDescription(InputMessage& msg, int32 x, int32 y, int32 { int startz, endz, zstep, skip = 0; - if(z > 7) { - startz = z - 2; - endz = (15 < z+2) ? 15 : z+2; + if(z > Otc::SEA_FLOOR) { + startz = z - Otc::AWARE_UNDEGROUND_FLOOR_RANGE; + endz = std::min(z + Otc::AWARE_UNDEGROUND_FLOOR_RANGE, (int)Otc::MAX_Z); zstep = 1; } else { - startz = 7; + startz = Otc::SEA_FLOOR; endz = 0; zstep = -1; } @@ -952,13 +958,17 @@ void ProtocolGame::setFloorDescription(InputMessage& msg, int32 x, int32 y, int3 for(int nx = 0; nx < width; nx++) { for(int ny = 0; ny < height; ny++) { + Position tilePos(x + nx + offset, y + ny + offset, z); + + // clean pre stored tiles + g_map.cleanTile(tilePos); + if(skip == 0) { int tileOpt = msg.getU16(true); if(tileOpt >= 0xFF00) skip = (msg.getU16() & 0xFF); else { - Position pos(x + nx + offset, y + ny + offset, z); - setTileDescription(msg, pos); + setTileDescription(msg, tilePos); skip = (msg.getU16() & 0xFF); } } @@ -1041,7 +1051,7 @@ ThingPtr ProtocolGame::internalGetThing(InputMessage& msg) if(knownCreature) creature = knownCreature; else - logTraceError("server says creature is known, but its not on creatures list"); + logTraceError("server said that a creature is known, but it's not"); } else if(thingId == 0x0061) { //creature is not known uint removeId = msg.getU32(); uint id = msg.getU32(); @@ -1065,6 +1075,8 @@ ThingPtr ProtocolGame::internalGetThing(InputMessage& msg) creature->setId(id); creature->setName(name); + + g_map.addCreature(creature); } uint8 healthPercent = msg.getU8(); @@ -1119,7 +1131,7 @@ ItemPtr ProtocolGame::internalGetItem(InputMessage& msg, int id) ItemPtr item = Item::create(id); if(item->isStackable() || item->isFluidContainer() || item->isFluid()) - item->setData(msg.getU8()); + item->setCountOrSubType(msg.getU8()); return item; } diff --git a/src/otclient/net/protocolgamesend.cpp b/src/otclient/net/protocolgamesend.cpp index 8f1b493f..3ef16737 100644 --- a/src/otclient/net/protocolgamesend.cpp +++ b/src/otclient/net/protocolgamesend.cpp @@ -341,24 +341,24 @@ void ProtocolGame::sendLookAt(const Position& position, int thingId, int stackpo send(oMsg); } -void ProtocolGame::sendTalk(const std::string& speakTypeDesc, int channelId, const std::string& receiver, const std::string& message) +void ProtocolGame::sendTalk(Otc::SpeakType speakType, int channelId, const std::string& receiver, const std::string& message) { if(message.length() > 255 || message.length() <= 0) return; - int speakType = Proto::translateSpeakTypeDesc(speakTypeDesc); + int serverSpeakType = Proto::translateSpeakTypeToServer(speakType); OutputMessage oMsg; oMsg.addU8(Proto::ClientTalk); - oMsg.addU8(speakType); + oMsg.addU8(serverSpeakType); - switch(speakType) { - case Proto::SpeakPrivate: - case Proto::SpeakPrivateRed: + switch(serverSpeakType) { + case Proto::ServerSpeakPrivate: + case Proto::ServerSpeakPrivateRed: oMsg.addString(receiver); break; - case Proto::SpeakChannelYellow: - case Proto::SpeakChannelRed: + case Proto::ServerSpeakChannelYellow: + case Proto::ServerSpeakChannelRed: oMsg.addU16(channelId); break; } @@ -390,10 +390,10 @@ void ProtocolGame::sendLeaveChannel(int channelId) send(oMsg); } -void ProtocolGame::sendPrivateChannel(const std::string& receiver) +void ProtocolGame::sendOpenPrivateChannel(const std::string& receiver) { OutputMessage oMsg; - oMsg.addU8(Proto::ClientPrivateChannel); + oMsg.addU8(Proto::ClientOpenPrivateChannel); oMsg.addString(receiver); send(oMsg); } diff --git a/src/otclient/otclient.cpp b/src/otclient/otclient.cpp index 087e4dde..bc84d753 100644 --- a/src/otclient/otclient.cpp +++ b/src/otclient/otclient.cpp @@ -24,6 +24,7 @@ #include #include "core/game.h" #include +#include "core/map.h" OTClient::OTClient(const std::string& appName) : Application(appName) { @@ -40,6 +41,8 @@ void OTClient::init(const std::vector& args) g_modules.ensureModuleLoaded("client"); g_modules.autoLoadModules(1000); + g_map.load(); + // load otclientrc.lua if(g_resources.fileExists("/otclientrc.lua")) { try { diff --git a/src/otclient/ui/uicreature.cpp b/src/otclient/ui/uicreature.cpp index 4b6debf2..db7a5fd6 100644 --- a/src/otclient/ui/uicreature.cpp +++ b/src/otclient/ui/uicreature.cpp @@ -30,7 +30,7 @@ void UICreature::draw() if(m_creature) { g_painter.setColor(Fw::white); - m_creature->draw(m_rect.bottomRight() - Point(32, 32) + Point(m_padding.left, m_padding.top), m_rect); + m_creature->draw(m_rect.bottomRight() - Point(32, 32) + Point(m_padding.left, m_padding.top), 1, false); } drawChildren(); diff --git a/src/otclient/ui/uigame.cpp b/src/otclient/ui/uigame.cpp index ded52551..40a67707 100644 --- a/src/otclient/ui/uigame.cpp +++ b/src/otclient/ui/uigame.cpp @@ -32,6 +32,7 @@ bool UIGame::onKeyPress(uchar keyCode, int keyboardModifiers, bool wouldFilter) UILineEditPtr chatLineEdit = std::dynamic_pointer_cast(getParent()->recursiveGetChildById("consoleLineEdit")); + //TODO: move this whole shit to lua if(keyboardModifiers == Fw::KeyboardNoModifier) { if(keyCode == Fw::KeyUp || keyCode == Fw::KeyNumpad8) { g_game.walk(Otc::North); diff --git a/src/otclient/ui/uiitem.cpp b/src/otclient/ui/uiitem.cpp index 2eff3049..0810b925 100644 --- a/src/otclient/ui/uiitem.cpp +++ b/src/otclient/ui/uiitem.cpp @@ -38,10 +38,10 @@ void UIItem::draw() Point topLeft = m_rect.bottomRight() - Point(32, 32) + Point(m_padding.left, m_padding.top); g_painter.setColor(Fw::white); - m_item->draw(topLeft, m_rect); + m_item->draw(topLeft, 1, true); - if(m_font && m_item->isStackable() && m_item->getData() > 1) { - std::string count = Fw::tostring(m_item->getData()); + if(m_font && m_item->isStackable() && m_item->getCount() > 1) { + std::string count = Fw::tostring(m_item->getCount()); m_font->renderText(count, Rect(m_rect.topLeft(), m_rect.bottomRight() - Point(3, 0)), Fw::AlignBottomRight, Color(231, 231, 231)); } @@ -51,3 +51,30 @@ void UIItem::draw() drawChildren(); } + +void UIItem::setItemId(int id) +{ + if(!m_item) + m_item = Item::create(id); + else { + // remove item + if(id == 0) + m_item = nullptr; + else + m_item->setId(id); + } +} + +void UIItem::onStyleApply(const std::string& styleName, const OTMLNodePtr& styleNode) +{ + UIWidget::onStyleApply(styleName, styleNode); + + for(const OTMLNodePtr& node : styleNode->children()) { + if(node->tag() == "item-id") + setItemId(node->value()); + else if(node->tag() == "item-count") + setItemCount(node->value()); + else if(node->tag() == "virtual") + setVirtual(node->value()); + } +} diff --git a/src/otclient/ui/uiitem.h b/src/otclient/ui/uiitem.h index c16cc0fd..b0d575e9 100644 --- a/src/otclient/ui/uiitem.h +++ b/src/otclient/ui/uiitem.h @@ -33,12 +33,24 @@ public: UIItem(); void draw(); + void setItemId(int id); + void setItemCount(int count) { if(m_item) m_item->setCount(count); } + void setItemSubType(int subType) { if(m_item) m_item->setSubType(subType); } void setItem(const ItemPtr& item) { m_item = item; } + void setVirtual(bool virt) { m_virtual = virt; } + void clearItem() { setItemId(0); } + int getItemId() { return m_item ? m_item->getId() : 0; } + int getItemCount() { return m_item ? m_item->getCount() : 0; } + int getItemSubType() { return m_item ? m_item->getSubType() : 0; } ItemPtr getItem() { return m_item; } + bool isVirtual() { return m_virtual; } protected: + void onStyleApply(const std::string& styleName, const OTMLNodePtr& styleNode); + ItemPtr m_item; + Boolean m_virtual; }; #endif diff --git a/src/otclient/ui/uimap.cpp b/src/otclient/ui/uimap.cpp index 45ca39d3..eccd9269 100644 --- a/src/otclient/ui/uimap.cpp +++ b/src/otclient/ui/uimap.cpp @@ -23,6 +23,7 @@ #include "uimap.h" #include #include +#include #include #include #include @@ -30,21 +31,79 @@ UIMap::UIMap() { m_dragable = true; + m_mapView = MapViewPtr(new MapView); + g_map.addMapView(m_mapView); + m_mapView->followCreature(g_game.getLocalPlayer()); +} + +UIMap::~UIMap() +{ + g_map.removeMapView(m_mapView); } void UIMap::draw() { drawSelf(); + // draw map border g_painter.setColor(Fw::black); g_painter.drawBoundingRect(m_mapRect.expanded(1)); - g_map.draw(m_mapRect); + + m_mapView->draw(m_mapRect); drawChildren(); } +void UIMap::zoomIn() +{ + 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 = getChildrenRect().expanded(-1); + Size mapSize = m_mapView->getVisibleSize(); + mapSize.scale(mapRect.size(), Fw::KeepAspectRatio); + + m_mapRect.resize(mapSize); + m_mapRect.moveCenter(m_rect.center()); +} + +void UIMap::zoomOut() +{ + 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; + + m_mapView->setVisibleDimension(Size(dimensionWidth, dimensionHeight)); + + Rect mapRect = getChildrenRect().expanded(-1); + Size mapSize = m_mapView->getVisibleSize(); + mapSize.scale(mapRect.size(), Fw::KeepAspectRatio); + + m_mapRect.resize(mapSize); + m_mapRect.moveCenter(m_rect.center()); +} + +void UIMap::setCameraPosition(const Position& pos) +{ + m_mapView->setCameraPosition(pos); +} + TilePtr UIMap::getTile(const Point& mousePos) { + /* if(!m_mapRect.contains(mousePos)) return nullptr; @@ -55,12 +114,12 @@ TilePtr UIMap::getTile(const Point& mousePos) if(localPlayer) relativeStretchMousePos += localPlayer->getWalkOffset(); - Size mapSize(g_map.getVibibleSize().width() * Map::NUM_TILE_PIXELS, g_map.getVibibleSize().height() * Map::NUM_TILE_PIXELS); + Size mapSize(g_map.getVibibleSize().width() * Otc::TILE_PIXELS, g_map.getVibibleSize().height() * Otc::TILE_PIXELS); PointF stretchFactor(m_mapRect.width() / (float)mapSize.width(), m_mapRect.height() / (float)mapSize.height()); PointF relativeMousePos = PointF(relativeStretchMousePos.x, relativeStretchMousePos.y) / stretchFactor; - PointF tilePosF = relativeMousePos / Map::NUM_TILE_PIXELS; + PointF tilePosF = relativeMousePos / Otc::TILE_PIXELS; Position tilePos = Position(1 + (int)tilePosF.x - g_map.getCentralOffset().x, 1 + (int)tilePosF.y - g_map.getCentralOffset().y, 0) + g_map.getCentralPosition(); if(!tilePos.isValid()) return nullptr; @@ -70,7 +129,7 @@ TilePtr UIMap::getTile(const Point& mousePos) // We must check every floor, from top to bottom to check for a clickable tile int firstFloor = g_map.getFirstVisibleFloor(); - tilePos.perspectiveUp(tilePos.z - firstFloor); + tilePos.coveredUp(tilePos.z - firstFloor); for(int i = firstFloor; i <= Map::MAX_Z; i++) { tile = g_map.getTile(tilePos); if(tile && tile->isClickable()) @@ -84,12 +143,14 @@ TilePtr UIMap::getTile(const Point& mousePos) return nullptr; return tile; + */ + return nullptr; } void UIMap::onGeometryChange(const Rect& oldRect, const Rect& newRect) { Rect mapRect = getChildrenRect().expanded(-1); - Size mapSize(g_map.getVibibleSize().width() * Map::NUM_TILE_PIXELS, g_map.getVibibleSize().height() * Map::NUM_TILE_PIXELS); + Size mapSize = m_mapView->getVisibleSize(); mapSize.scale(mapRect.size(), Fw::KeepAspectRatio); m_mapRect.resize(mapSize); diff --git a/src/otclient/ui/uimap.h b/src/otclient/ui/uimap.h index 4e0f6ab1..41452cc9 100644 --- a/src/otclient/ui/uimap.h +++ b/src/otclient/ui/uimap.h @@ -31,14 +31,21 @@ class UIMap : public UIWidget { public: UIMap(); + ~UIMap(); + void draw(); + void zoomIn(); + void zoomOut(); + void setCameraPosition(const Position& pos); + TilePtr getTile(const Point& mousePos); protected: virtual void onGeometryChange(const Rect& oldRect, const Rect& newRect); private: + MapViewPtr m_mapView; Rect m_mapRect; }; diff --git a/src/otclient/util/position.h b/src/otclient/util/position.h index bcbdd50d..67bf8178 100644 --- a/src/otclient/util/position.h +++ b/src/otclient/util/position.h @@ -30,51 +30,99 @@ class Position { public: - Position() : x(-1), y(-1), z(-1) { } - Position(int x, int y, int z) : x(x), y(y), z(z) { } + Position() : x(65535), y(65535), z(255) { } + Position(uint16 x, uint16 y, uint8 z) : x(x), y(y), z(z) { } - static Position getPosFromDirection(Otc::Direction direction) { + Position translatedToDirection(Otc::Direction direction) { + Position pos = *this; switch(direction) { case Otc::North: - return Position( 0, -1, 0); + pos.y--; + break; case Otc::East: - return Position( 1, 0, 0); + pos.x++; + break; case Otc::South: - return Position( 0, 1, 0); + pos.y++; + break; case Otc::West: - return Position(-1, 0, 0); + pos.x--; + break; case Otc::NorthEast: - return Position( 1, -1, 0); + pos.x++; + pos.y--; + break; case Otc::SouthEast: - return Position( 1, 1, 0); + pos.x++; + pos.y++; + break; case Otc::SouthWest: - return Position(-1, 1, 0); + pos.x--; + pos.y++; + break; case Otc::NorthWest: - return Position(-1, -1, 0); - default: - return Position(); + pos.x--; + pos.y--; + break; } + return pos; + } + + Position translatedToReverseDirection(Otc::Direction direction) { + Position pos = *this; + switch(direction) { + case Otc::North: + pos.y++; + break; + case Otc::East: + pos.x--; + break; + case Otc::South: + pos.y--; + break; + case Otc::West: + pos.x++; + break; + case Otc::NorthEast: + pos.x--; + pos.y++; + break; + case Otc::SouthEast: + pos.x--; + pos.y--; + break; + case Otc::SouthWest: + pos.x++; + pos.y--; + break; + case Otc::NorthWest: + pos.x++; + pos.y++; + break; + } + return pos; } Otc::Direction getDirectionFromPosition(const Position& position) const { - Position positionDelta = position - *this; + int dx = position.x - x; + int dy = position.y - y; - if(positionDelta.x == 0 && positionDelta.y == 0) + if(dx == 0 && dy == 0) return Otc::InvalidDirection; - else if(positionDelta.x == 0) { - if(positionDelta.y < 0) + else if(dx == 0) { + if(dy < 0) return Otc::North; - else if(positionDelta.y > 0) + else if(dy > 0) return Otc::South; } - else if(positionDelta.y == 0) { - if(positionDelta.x < 0) + else if(dy == 0) { + if(dx < 0) return Otc::West; - else if(positionDelta.x > 0) + else if(dx > 0) return Otc::East; } else { - float angle = std::atan2(positionDelta.y * -1, positionDelta.x) * RAD_TO_DEC; + float angle = std::atan2(dy * -1, dx) * RAD_TO_DEC; if(angle < 0) angle += 360; @@ -98,7 +146,10 @@ public: return Otc::InvalidDirection; } - bool isValid() const { return x >= 0 && y >= 0 && z >= 0 && x <= 65535 && y <= 65535 && z <= 255; } + bool isValid() const { return !(x == 65535 && y == 65535 && z == 255); } + + void translate(int dx, int dy, short dz = 0) { x += dx; y += dy; z += dz; } + Position translated(int dx, int dy, short dz = 0) const { Position pos = *this; pos.x += dx; pos.y += dy; pos.z += dz; return pos; } Position operator+(const Position& other) const { return Position(x + other.x, y + other.y, z + other.z); } Position& operator+=(const Position& other) { x+=other.x; y+=other.y; z +=other.z; return *this; } @@ -109,19 +160,50 @@ public: bool operator==(const Position& other) const { return other.x == x && other.y == y && other.z == z; } bool operator!=(const Position& other) const { return other.x!=x || other.y!=y || other.z!=z; } - bool isInRange(const Position& pos, int xdif, int ydif, int zdif = 1) const { - return std::abs(x-pos.x) <= xdif && std::abs(y-pos.y) <= ydif && std::abs(pos.z-z) <= zdif; + bool isInRange(const Position& pos, int xRange, int yRange) const { return std::abs(x-pos.x) <= xRange && std::abs(y-pos.y) <= yRange && z == pos.z; } + bool isInRange(const Position& pos, int minXRange, int maxXRange, int minYRange, int maxYRange) const { + return (pos.x >= x-minXRange && pos.x <= x+maxXRange && pos.y >= y-minYRange && pos.y <= y+maxYRange && pos.z == z); + } + + bool up(int n = 1) { + int nz = z-n; + if(nz >= 0 && nz <= Otc::MAX_Z) { + z = nz; + return true; + } + return false; } - void up(int n = 1) { z-=n; } - void down(int n = 1) { z+=n; } + bool down(int n = 1) { + int nz = z+n; + if(nz >= 0 && nz <= Otc::MAX_Z) { + z = nz; + return true; + } + return false; + } - void perspectiveUp(int n = 1) { x+=n; y+=n; z-=n; } - void coveredDown(int n = 1) { x-=n; y-=n; z+=n; } + bool coveredUp(int n = 1) { + int nx = x+n, ny = y+n, nz = z-n; + if(nx >= 0 && nx <= 65535 && ny >= 0 && ny <= 65535 && nz >= 0 && nz <= Otc::MAX_Z) { + x = nx; y = ny; z = nz; + return true; + } + return false; + } + + bool coveredDown(int n = 1) { + int nx = x-n, ny = y-n, nz = z+n; + if(nx >= 0 && nx <= 65535 && ny >= 0 && ny <= 65535 && nz >= 0 && nz <= Otc::MAX_Z) { + x = nx; y = ny; z = nz; + return true; + } + return false; + } - int x; - int y; - int z; + uint16 x; + uint16 y; + uint8 z; }; struct PositionHasher : std::unary_function { @@ -132,13 +214,17 @@ struct PositionHasher : std::unary_function { inline std::ostream& operator<<(std::ostream& out, const Position& pos) { - out << pos.x << " " << pos.y << " " << pos.z; + out << (int)pos.x << " " << (int)pos.y << " " << (int)pos.z; return out; } inline std::istream& operator>>(std::istream& in, Position& pos) { - in >> pos.x >> pos.y >> pos.z; + int x, y, z; + in >> x >> y >> z; + pos.x = x; + pos.y = y; + pos.z = z; return in; }