merge revgraphics

master
Eduardo Bart 12 years ago
commit 7bb828faee

@ -173,6 +173,11 @@ end
function Terminal.executeCommand(command) function Terminal.executeCommand(command)
if command == nil or #command == 0 then return end if command == nil or #command == 0 then return end
logLocked = true
Logger.log(LogInfo, '>> ' .. command)
logLocked = false
-- detect and convert commands with simple syntax -- detect and convert commands with simple syntax
local realCommand local realCommand
if commandEnv[command] then if commandEnv[command] then

@ -1 +1 @@
Subproject commit dd648e1431171bffe091b748744395780df7eba1 Subproject commit 9beb17daaeb170c127c39c5a5e4feb9d95ebed92

@ -298,3 +298,18 @@ KeyCodeDescs = {
[KeyNumpad8] = 'Numpad8', [KeyNumpad8] = 'Numpad8',
[KeyNumpad9] = 'Numpad9' [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

@ -1,5 +1,13 @@
Settings = {} 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) local function convertSettingValue(value)
if type(value) == 'table' then if type(value) == 'table' then
if value.x and value.width then if value.x and value.width then
@ -20,21 +28,10 @@ local function convertSettingValue(value)
end end
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) function Settings.set(key, value)
g_configs.set(key, convertSettingValue(value)) g_configs.set(key, convertSettingValue(value))
end end
function Settings.setList(key, list)
g_configs.setList(key, list)
end
function Settings.setDefault(key, value) function Settings.setDefault(key, value)
if Settings.exists(key) then return false end if Settings.exists(key) then return false end
@ -49,10 +46,6 @@ function Settings.get(key, default)
return g_configs.get(key) return g_configs.get(key)
end end
function Settings.getList(key)
return g_configs.getList(key)
end
function Settings.getString(key, default) function Settings.getString(key, default)
return Settings.get(key, default) return Settings.get(key, default)
end end

@ -13,7 +13,7 @@ local function moveToolTip(tooltip)
else else
pos.x = pos.x + 10 pos.x = pos.x + 10
end end
tooltip:setPos(pos) tooltip:setPosition(pos)
end end
-- public functions -- public functions

@ -1,6 +1,8 @@
function UIItem:onDragEnter(mousePos) function UIItem:onDragEnter(mousePos)
if self:isVirtual() then return false end
local item = self:getItem() local item = self:getItem()
if not item then return false end if not item then return true end
self:setBorderWidth(1) self:setBorderWidth(1)
@ -11,28 +13,32 @@ function UIItem:onDragEnter(mousePos)
end end
function UIItem:onDragLeave(widget, mousePos) function UIItem:onDragLeave(widget, mousePos)
if self:isVirtual() then return false end
if not self.parsed then if not self.parsed then
self.currentDragThing = nil self.currentDragThing = nil
end end
restoreCursor() restoreCursor()
self:setBorderWidth(0) self:setBorderWidth(0)
return true return true
end end
function UIItem:onDrop(widget, mousePos) 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 pos = self.position
local data = widget.currentDragThing:getData() local count = widget.currentDragThing:getCount()
if widget.currentDragThing:isStackable() and data > 1 then if widget.currentDragThing:isStackable() and count > 1 then
widget.parsed = true widget.parsed = true
local moveWindow = displayUI('/game/movewindow.otui') local moveWindow = displayUI('/game/movewindow.otui')
local spinbox = moveWindow:getChildById('spinbox') local spinbox = moveWindow:getChildById('spinbox')
spinbox:setMaximum(data) spinbox:setMaximum(count)
spinbox:setMinimum(1) spinbox:setMinimum(1)
spinbox:setCurrentIndex(data) spinbox:setCurrentIndex(count)
local okButton = moveWindow:getChildById('buttonOk') local okButton = moveWindow:getChildById('buttonOk')
okButton.onClick = function() Game.move(widget.currentDragThing, pos, spinbox:getCurrentIndex()) okButton:getParent():destroy() widget.currentDragThing = nil end okButton.onClick = function() Game.move(widget.currentDragThing, pos, spinbox:getCurrentIndex()) okButton:getParent():destroy() widget.currentDragThing = nil end
moveWindow.onEnter = okButton.onClick moveWindow.onEnter = okButton.onClick
@ -45,6 +51,8 @@ function UIItem:onDrop(widget, mousePos)
end end
function UIItem:onHoverChange(hovered) function UIItem:onHoverChange(hovered)
if self:isVirtual() then return end
if g_ui.getDraggingWidget() and self ~= g_ui.getDraggingWidget() then if g_ui.getDraggingWidget() and self ~= g_ui.getDraggingWidget() then
if hovered then if hovered then
self:setBorderWidth(1) self:setBorderWidth(1)
@ -55,6 +63,8 @@ function UIItem:onHoverChange(hovered)
end end
function UIItem:onMouseRelease(mousePosition, mouseButton) function UIItem:onMouseRelease(mousePosition, mouseButton)
if self:isVirtual() then return false end
local item = self:getItem() local item = self:getItem()
if not item or not self:containsPoint(mousePosition) then return false end if not item or not self:containsPoint(mousePosition) then return false end
return Game.processMouseAction(mousePosition, mouseButton, nil, item, item, nil, item) return Game.processMouseAction(mousePosition, mouseButton, nil, item, item, nil, item)

@ -65,7 +65,7 @@ function UIPopupMenu:onKeyPress(keyCode, keyboardModifiers, wouldFilter)
return false return false
end end
-- close all menus when the window is resized -- close all menus when the window is resized
local function onRootGeometryUpdate() local function onRootGeometryUpdate()
for i,menu in ipairs(displayedMenuList) do for i,menu in ipairs(displayedMenuList) do
menu:destroy() menu:destroy()

@ -21,12 +21,12 @@ local function onUseWithMouseRelease(self, mousePosition, mouseButton)
if mouseButton == MouseLeftButton then if mouseButton == MouseLeftButton then
local clickedWidget = Game.gameUi:recursiveGetChildByPos(mousePosition) local clickedWidget = Game.gameUi:recursiveGetChildByPos(mousePosition)
if clickedWidget then if clickedWidget then
if clickedWidget.getTile then if clickedWidget:getClassName() == 'UIMap' then
local tile = clickedWidget:getTile(mousePosition) local tile = clickedWidget:getTile(mousePosition)
if tile then if tile then
Game.useWith(Game.selectedThing, tile:getTopMultiUseThing()) Game.useWith(Game.selectedThing, tile:getTopMultiUseThing())
end end
elseif clickedWidget.getItem then elseif clickedWidget:getClassName() == 'UIItem' and not clickedWidget:isVirtual() then
Game.useWith(Game.selectedThing, clickedWidget:getItem()) Game.useWith(Game.selectedThing, clickedWidget:getItem())
end end
end end

@ -15,7 +15,7 @@ function UIMap:onDragLeave(widget, mousePos)
if not self.parsed then if not self.parsed then
self.currentDragThing = nil self.currentDragThing = nil
end end
restoreCursor() restoreCursor()
return true return true
end end
@ -25,21 +25,21 @@ function UIMap:onDrop(widget, mousePos)
local tile = self:getTile(mousePos) local tile = self:getTile(mousePos)
if not tile then return false end 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 if widget.currentDragThing:isStackable() and data > 1 then
widget.parsed = true widget.parsed = true
local moveWindow = displayUI('/game/movewindow.otui') local moveWindow = displayUI('/game/movewindow.otui')
local spinbox = moveWindow:getChildById('spinbox') local spinbox = moveWindow:getChildById('spinbox')
spinbox:setMaximum(data) spinbox:setMaximum(count)
spinbox:setMinimum(1) spinbox:setMinimum(1)
spinbox:setCurrentIndex(data) spinbox:setCurrentIndex(count)
local okButton = moveWindow:getChildById('buttonOk') 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 moveWindow.onEnter = okButton.onClick
else else
Game.move(widget.currentDragThing, tile:getPos(), 1) Game.move(widget.currentDragThing, tile:getPosition(), 1)
end end
return true return true

@ -1,11 +1,11 @@
function Thing:isInsideContainer() function Thing:isInsideContainer()
local pos = self:getPos() local pos = self:getPosition()
return (pos and pos.x == 65535 and pos.y >= 64) return (pos and pos.x == 65535 and pos.y >= 64)
end end
function Thing:getContainerId() function Thing:getContainerId()
local pos = self:getPos() local pos = self:getPosition()
if not pos then return 0 end if not pos then return 0 end
return pos.y - 64 return pos.y - 64
end end

@ -46,7 +46,7 @@ function Containers.onContainerOpen(containerId, itemId, name, capacity, hasPare
if i <= #items then if i <= #items then
local item = items[i] local item = items[i]
item:setPos(itemWidget.position) item:setPosition(itemWidget.position)
itemWidget:setItem(item) itemWidget:setItem(item)
end end
end end
@ -76,7 +76,7 @@ function Containers.onContainerAddItem(containerId, item)
local swapItem = itemWidget:getItem() local swapItem = itemWidget:getItem()
if swapItem then if swapItem then
swapItem:setPos(nextItemWidget.position) swapItem:setPosition(nextItemWidget.position)
nextItemWidget:setItem(swapItem) nextItemWidget:setItem(swapItem)
end end
@ -85,7 +85,7 @@ function Containers.onContainerAddItem(containerId, item)
local itemWidget = container:getChildByIndex(1) local itemWidget = container:getChildByIndex(1)
if not itemWidget then return end if not itemWidget then return end
item:setPos(itemWidget.position) item:setPosition(itemWidget.position)
itemWidget:setItem(item) itemWidget:setItem(item)
container.itemCount = container.itemCount + 1 container.itemCount = container.itemCount + 1
@ -98,7 +98,7 @@ function Containers.onContainerUpdateItem(containerId, slot, item)
local itemWidget = container:getChildByIndex(slot + 1) local itemWidget = container:getChildByIndex(slot + 1)
if not itemWidget then return end if not itemWidget then return end
itemWidget:setItem(item) itemWidget:setItem(item)
item:setPos(itemWidget.position) item:setPosition(itemWidget.position)
end end
function Containers.onContainerRemoveItem(containerId, slot) function Containers.onContainerRemoveItem(containerId, slot)
@ -117,9 +117,9 @@ function Containers.onContainerRemoveItem(containerId, slot)
if not nextItemWidget then return end if not nextItemWidget then return end
local item = nextItemWidget:getItem() local item = nextItemWidget:getItem()
local pos = item:getPos() local pos = item:getPosition()
pos.z = pos.z - 1 pos.z = pos.z - 1
item:setPos(pos) item:setPosition(pos)
itemWidget:setItem(item) itemWidget:setItem(item)
nextItemWidget:setItem(nil) nextItemWidget:setItem(nil)

@ -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;
}

@ -11,24 +11,19 @@ uniform vec4 bodyColor;
uniform vec4 legsColor; uniform vec4 legsColor;
uniform vec4 feetColor; 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 calcOutfitPixel()
{ {
vec4 pixel = texture2D(texture, textureCoords); vec4 pixel = texture2D(texture, textureCoords);
vec4 maskColor = texture2D(maskTexture, textureCoords); vec4 maskColor = texture2D(maskTexture, textureCoords);
vec4 outColor = vec4(1.0, 1.0, 1.0, 1.0); 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; outColor = headColor;
else if(maskColor == red) else if(maskColor.r > 0.1)
outColor = bodyColor; outColor = bodyColor;
else if(maskColor == green) else if(maskColor.g > 0.1)
outColor = legsColor; outColor = legsColor;
else if(maskColor == blue) else if(maskColor.b > 0.1)
outColor = feetColor; outColor = feetColor;
return pixel * outColor; return pixel * outColor;

@ -112,3 +112,22 @@ void ConfigManager::remove(const std::string& key)
if(child) if(child)
m_confsDoc->removeChild(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);
}

@ -37,6 +37,11 @@ public:
void setList(const std::string& key, const std::vector<std::string>& list); void setList(const std::string& key, const std::vector<std::string>& list);
std::string get(const std::string& key); std::string get(const std::string& key);
std::vector<std::string> getList(const std::string& key); std::vector<std::string> 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); bool exists(const std::string& key);
void remove(const std::string& key); void remove(const std::string& key);

@ -28,7 +28,8 @@ EventDispatcher g_dispatcher;
void EventDispatcher::flush() void EventDispatcher::flush()
{ {
poll(); while(!m_eventList.empty())
poll();
while(!m_scheduledEventList.empty()) while(!m_scheduledEventList.empty())
m_scheduledEventList.pop(); m_scheduledEventList.pop();
@ -44,7 +45,8 @@ void EventDispatcher::poll()
scheduledEvent->execute(); scheduledEvent->execute();
} }
while(!m_eventList.empty()) { int maxEvents = m_eventList.size();
for(int i=0;i<maxEvents;++i) {
EventPtr event = m_eventList.front(); EventPtr event = m_eventList.front();
m_eventList.pop_front(); m_eventList.pop_front();
event->execute(); event->execute();

@ -33,7 +33,7 @@ public:
Event(const SimpleCallback& callback) : m_callback(callback), m_canceled(false), m_executed(false) { } Event(const SimpleCallback& callback) : m_callback(callback), m_canceled(false), m_executed(false) { }
void execute() { void execute() {
if(!m_canceled) { if(!m_canceled && !m_executed) {
m_callback(); m_callback();
m_executed = true; m_executed = true;
} }

@ -123,7 +123,7 @@ bool ResourceManager::saveFile(const std::string& fileName, const uchar* data, u
return true; 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(); std::streampos oldPos = in.tellg();
in.seekg(0, std::ios::end); in.seekg(0, std::ios::end);

@ -44,7 +44,7 @@ public:
bool saveFile(const std::string& fileName, const uchar* data, uint size); 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, 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); bool deleteFile(const std::string& fileName);

@ -26,6 +26,15 @@
uint FrameBuffer::boundFbo = 0; 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) FrameBuffer::FrameBuffer(const Size& size)
{ {
m_clearColor = Fw::alpha; m_clearColor = Fw::alpha;
@ -44,6 +53,9 @@ FrameBuffer::~FrameBuffer()
void FrameBuffer::resize(const Size& size) void FrameBuffer::resize(const Size& size)
{ {
if(!size.isValid())
return;
if(m_texture && m_texture->getSize() == size) if(m_texture && m_texture->getSize() == size)
return; return;
@ -83,6 +95,16 @@ void FrameBuffer::release()
g_graphics.setViewportSize(m_oldViewportSize); 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) void FrameBuffer::draw(const Rect& dest)
{ {
g_painter.drawTexturedRect(dest, m_texture); g_painter.drawTexturedRect(dest, m_texture);

@ -24,21 +24,26 @@
#define FRAMEBUFFER_H #define FRAMEBUFFER_H
#include "declarations.h" #include "declarations.h"
#include "texture.h"
class FrameBuffer class FrameBuffer
{ {
public: public:
FrameBuffer();
FrameBuffer(const Size& size); FrameBuffer(const Size& size);
virtual ~FrameBuffer(); virtual ~FrameBuffer();
void resize(const Size& size); void resize(const Size& size);
void bind(bool clear = true); void bind(bool clear = true);
void release(); void release();
void generateMipmaps();
void draw(const Rect& dest); void draw(const Rect& dest);
void draw(const Rect& dest, const Rect& src);
void setClearColor(const Color& color) { m_clearColor = color; } void setClearColor(const Color& color) { m_clearColor = color; }
TexturePtr getTexture() { return m_texture; } TexturePtr getTexture() { return m_texture; }
const Size& getSize() { return m_texture->getSize(); }
private: private:
void internalBind(); void internalBind();

@ -106,3 +106,10 @@ void Graphics::setViewportSize(const Size& size)
m_viewportSize = size; m_viewportSize = size;
} }
int Graphics::getMaxTextureSize()
{
static GLint maxTexSize = -1;
if(maxTexSize == -1)
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize);
return maxTexSize;
}

@ -40,6 +40,7 @@ public:
void setViewportSize(const Size& size); void setViewportSize(const Size& size);
int getMaxTextureSize();
const Size& getViewportSize() { return m_viewportSize; } const Size& getViewportSize() { return m_viewportSize; }
TexturePtr getEmptyTexture() { return m_emptyTexture; } TexturePtr getEmptyTexture() { return m_emptyTexture; }

@ -60,8 +60,8 @@ void Painter::drawProgram(const PainterShaderProgramPtr& program, CoordsBuffer&
return; return;
program->setProjectionMatrix(m_projectionMatrix); program->setProjectionMatrix(m_projectionMatrix);
program->setOpacity(m_currentOpacity); program->setOpacity(m_opacity);
program->setColor(m_currentColor); program->setColor(m_color);
program->draw(coordsBuffer, drawMode); program->draw(coordsBuffer, drawMode);
} }
@ -141,4 +141,32 @@ void Painter::setCompositionMode(Painter::CompositionMode compositionMode)
glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA); glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA);
break; 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;
} }

@ -50,11 +50,11 @@ public:
void drawFilledRect(const Rect& dest); void drawFilledRect(const Rect& dest);
void drawBoundingRect(const Rect& dest, int innerLineWidth = 1); void drawBoundingRect(const Rect& dest, int innerLineWidth = 1);
void setColor(const Color& color) { m_currentColor = color; } void setColor(const Color& color) { m_color = color; }
Color getColor() { return m_currentColor; } Color getColor() { return m_color; }
void setOpacity(float opacity) { m_currentOpacity = opacity; } void setOpacity(float opacity) { m_opacity = opacity; }
float getOpacity() { return m_currentOpacity; } float getOpacity() { return m_opacity; }
void setCustomProgram(PainterShaderProgramPtr program); void setCustomProgram(PainterShaderProgramPtr program);
void releaseCustomProgram() { m_customProgram = nullptr; } void releaseCustomProgram() { m_customProgram = nullptr; }
@ -64,14 +64,24 @@ public:
void setProjectionMatrix(const Matrix3& projectionMatrix) { m_projectionMatrix = projectionMatrix; } void setProjectionMatrix(const Matrix3& projectionMatrix) { m_projectionMatrix = projectionMatrix; }
Matrix3 getProjectionMatrix() { return m_projectionMatrix; } Matrix3 getProjectionMatrix() { return m_projectionMatrix; }
void saveAndResetState();
void restoreSavedState();
private: private:
PainterShaderProgramPtr m_drawTexturedProgram; PainterShaderProgramPtr m_drawTexturedProgram;
PainterShaderProgramPtr m_drawSolidColorProgram; PainterShaderProgramPtr m_drawSolidColorProgram;
PainterShaderProgramPtr m_customProgram; PainterShaderProgramPtr m_customProgram;
Matrix3 m_projectionMatrix; Matrix3 m_projectionMatrix;
Color m_currentColor; Color m_color;
float m_currentOpacity; float m_opacity;
CompositionMode m_compostionMode;
CoordsBuffer m_coordsBuffer; CoordsBuffer m_coordsBuffer;
PainterShaderProgramPtr m_oldCustomProgram;
Matrix3 m_oldProjectionMatrix;
Color m_oldColor;
float m_oldOpacity;
CompositionMode m_oldCompostionMode;
}; };
extern Painter g_painter; extern Painter g_painter;

@ -26,6 +26,11 @@
#include "texturemanager.h" #include "texturemanager.h"
#include <framework/core/clock.h> #include <framework/core/clock.h>
PainterShaderProgram::PainterShaderProgram()
{
m_textures.fill(std::make_tuple(-1, 0));
}
bool PainterShaderProgram::link() bool PainterShaderProgram::link()
{ {
bindAttributeLocation(VERTEX_COORDS_ATTR, "vertexCoord"); bindAttributeLocation(VERTEX_COORDS_ATTR, "vertexCoord");
@ -63,12 +68,9 @@ void PainterShaderProgram::setOpacity(float opacity)
void PainterShaderProgram::setUniformTexture(int location, const TexturePtr& texture, int index) void PainterShaderProgram::setUniformTexture(int location, const TexturePtr& texture, int index)
{ {
if(index > 0) assert(index >= 0 && index <= 1);
glActiveTexture(GL_TEXTURE0 + index);
glBindTexture(GL_TEXTURE_2D, texture ? texture->getId() : 0); m_textures[index] = std::make_tuple(location, texture ? texture->getId() : 0);
if(index > 0)
glActiveTexture(GL_TEXTURE0);
setUniformValue(location, index);
} }
void PainterShaderProgram::setTexture(const TexturePtr& texture) void PainterShaderProgram::setTexture(const TexturePtr& texture)
@ -112,6 +114,18 @@ void PainterShaderProgram::draw(const CoordsBuffer& coordsBuffer, DrawMode drawM
mustDisableTexCoordsArray = true; 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); glDrawArrays(drawMode, 0, numVertices);
if(mustDisableVertexArray) if(mustDisableVertexArray)

@ -45,6 +45,8 @@ public:
TriangleStrip = GL_TRIANGLE_STRIP TriangleStrip = GL_TRIANGLE_STRIP
}; };
PainterShaderProgram();
bool link(); bool link();
void setProjectionMatrix(const Matrix3& projectionMatrix); void setProjectionMatrix(const Matrix3& projectionMatrix);
@ -57,6 +59,7 @@ public:
private: private:
DrawMode m_drawMode; DrawMode m_drawMode;
Timer m_startTimer; Timer m_startTimer;
std::array<std::tuple<int, int>, 4> m_textures;
}; };
#endif #endif

@ -29,7 +29,7 @@ Particle::Particle(const Point& pos, const Size& startSize, const Size& finalSiz
m_colors = colors; m_colors = colors;
m_colorsStops = colorsStops; m_colorsStops = colorsStops;
m_pos = PointF(pos.x, pos.y); m_position = PointF(pos.x, pos.y);
m_startSize = startSize; m_startSize = startSize;
m_finalSize = finalSize; m_finalSize = finalSize;
m_velocity = velocity; m_velocity = velocity;
@ -80,18 +80,18 @@ void Particle::updatePosition(double elapsedTime)
PointF delta = m_velocity * elapsedTime; PointF delta = m_velocity * elapsedTime;
delta.y *= -1; // painter orientate Y axis in the inverse direction 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; mustRedraw = true;
m_pos += delta; m_position += delta;
} }
// update acceleration // update acceleration
m_velocity += m_acceleration * elapsedTime; 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() void Particle::updateSize()

@ -36,10 +36,10 @@ public:
bool hasFinished() { return m_finished; } bool hasFinished() { return m_finished; }
PointF getPos() { return m_pos; } PointF getPosition() { return m_position; }
PointF getVelocity() { return m_velocity; } 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; } void setVelocity(const PointF& velocity) { m_velocity = velocity; }
private: private:
@ -51,7 +51,7 @@ private:
std::vector<Color> m_colors; std::vector<Color> m_colors;
std::vector<float> m_colorsStops; std::vector<float> m_colorsStops;
TexturePtr m_texture; TexturePtr m_texture;
PointF m_pos; PointF m_position;
PointF m_velocity; PointF m_velocity;
PointF m_acceleration; PointF m_acceleration;
Size m_size, m_startSize, m_finalSize; Size m_size, m_startSize, m_finalSize;

@ -115,7 +115,7 @@ bool AttractionAffector::load(const OTMLNodePtr& node)
for(const OTMLNodePtr& childNode : node->children()) { for(const OTMLNodePtr& childNode : node->children()) {
if(childNode->tag() == "position") if(childNode->tag() == "position")
m_pos = childNode->value<Point>(); m_position = childNode->value<Point>();
else if(childNode->tag() == "acceleration") else if(childNode->tag() == "acceleration")
m_acceleration = childNode->value<float>(); m_acceleration = childNode->value<float>();
else if(childNode->tag() == "velocity-reduction-percent") else if(childNode->tag() == "velocity-reduction-percent")
@ -131,8 +131,8 @@ void AttractionAffector::updateParticle(const ParticlePtr& particle, double elap
if(!m_active) if(!m_active)
return; return;
PointF pPosition = particle->getPos(); PointF pPosition = particle->getPosition();
PointF d = PointF(m_pos.x - pPosition.x, pPosition.y - m_pos.y); PointF d = PointF(m_position.x - pPosition.x, pPosition.y - m_position.y);
if(d.length() == 0) if(d.length() == 0)
return; return;

@ -57,7 +57,7 @@ public:
void updateParticle(const ParticlePtr& particle, double elapsedTime); void updateParticle(const ParticlePtr& particle, double elapsedTime);
private: private:
Point m_pos; Point m_position;
float m_acceleration, m_reduction; float m_acceleration, m_reduction;
bool m_repelish; bool m_repelish;
}; };

@ -31,7 +31,7 @@ ParticleEmitter::ParticleEmitter(const ParticleSystemPtr& parent)
{ {
m_parent = parent; m_parent = parent;
m_pos = Point(0, 0); m_position = Point(0, 0);
m_duration = -1; m_duration = -1;
m_delay = 0; m_delay = 0;
m_burstRate = 1; m_burstCount = 32; m_burstRate = 1; m_burstCount = 32;
@ -65,7 +65,7 @@ bool ParticleEmitter::load(const OTMLNodePtr& node)
for(const OTMLNodePtr& childNode : node->children()) { for(const OTMLNodePtr& childNode : node->children()) {
// self related // self related
if(childNode->tag() == "position") if(childNode->tag() == "position")
m_pos = childNode->value<Point>(); m_position = childNode->value<Point>();
else if(childNode->tag() == "duration") else if(childNode->tag() == "duration")
m_duration = childNode->value<float>(); m_duration = childNode->value<float>();
else if(childNode->tag() == "delay") else if(childNode->tag() == "delay")
@ -199,7 +199,7 @@ void ParticleEmitter::update(double elapsedTime)
float pRadius = Fw::randomRange(m_pMinPositionRadius, m_pMaxPositionRadius); float pRadius = Fw::randomRange(m_pMinPositionRadius, m_pMaxPositionRadius);
float pAngle = Fw::randomRange(m_pMinPositionAngle, m_pMaxPositionAngle); 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) { for(int p = 0; p < m_burstCount; ++p) {

@ -44,7 +44,7 @@ private:
ParticleSystemWeakPtr m_parent; ParticleSystemWeakPtr m_parent;
// self related // self related
Point m_pos; Point m_position;
float m_duration, m_delay; float m_duration, m_delay;
double m_elapsedTime; double m_elapsedTime;
bool m_finished, m_active; bool m_finished, m_active;

@ -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_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// nearest filtering (non smooth) setupFilters();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
return id; return id;
} }
void Texture::generateMipmaps()
{
bind();
if(!m_useMipmaps) {
m_useMipmaps = true;
setupFilters();
}
glGenerateMipmap(GL_TEXTURE_2D);
}
void Texture::setSmooth(bool smooth) void Texture::setSmooth(bool smooth)
{ {
if(smooth == m_smooth) if(smooth == m_smooth)
return; return;
if(smooth) { m_smooth = smooth;
// enable smooth texture bind();
glBindTexture(GL_TEXTURE_2D, m_textureId); setupFilters();
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;
} }
std::vector<uint8> Texture::getPixels() std::vector<uint8> Texture::getPixels()
{ {
// hack to copy pixels from opengl memory
FrameBufferPtr fb(new FrameBuffer(m_size));
std::vector<uint8> pixels(m_size.area()*4, 0); std::vector<uint8> 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(); fb->bind();
g_painter.saveAndResetState();
g_painter.drawTexturedRect(Rect(0,0,m_size), shared_from_this()); 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]); glReadPixels(0, 0, m_size.width(), m_size.height(), GL_RGBA, GL_UNSIGNED_BYTE, &pixels[0]);
g_painter.restoreSavedState();
fb->release(); 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; return pixels;
} }
void Texture::generateBilinearMipmaps(std::vector<uint8> inPixels)
{
bind();
if(!m_useMipmaps) {
m_useMipmaps = true;
setupFilters();
}
Size inSize = getSize();
Size outSize = inSize / 2;
std::vector<uint8> outPixels(outSize.area()*4);
int mipmap = 1;
while(true) {
for(int x=0;x<outSize.width();++x) {
for(int y=0;y<outSize.height();++y) {
uint8 *inPixel[4];
inPixel[0] = &inPixels[((y*2)*inSize.width() + (x*2))*4];
inPixel[1] = &inPixels[((y*2)*inSize.width() + (x*2)+1)*4];
inPixel[2] = &inPixels[((y*2+1)*inSize.width() + (x*2))*4];
inPixel[3] = &inPixels[((y*2+1)*inSize.width() + (x*2)+1)*4];
uint8 *outPixel = &outPixels[(y*outSize.width() + x)*4];
int pixelsSum[4];
for(int i=0;i<4;++i)
pixelsSum[i] = 0;
int usedPixels = 0;
for(int j=0;j<4;++j) {
// ignore colors of complete alpha pixels
if(inPixel[j][3] < 16)
continue;
for(int i=0;i<4;++i)
pixelsSum[i] += inPixel[j][i];
usedPixels++;
}
for(int i=0;i<4;++i) {
if(usedPixels > 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);
}

@ -32,7 +32,14 @@ public:
Texture(int width, int height, int channels, uchar* pixels = NULL); Texture(int width, int height, int channels, uchar* pixels = NULL);
virtual ~Texture(); 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<uint8> inPixels);
void setSmooth(bool smooth);
GLuint getId() { return m_textureId; } GLuint getId() { return m_textureId; }
std::vector<uint8> getPixels(); std::vector<uint8> getPixels();
@ -44,10 +51,12 @@ public:
bool isEmpty() const { return m_textureId == 0; } bool isEmpty() const { return m_textureId == 0; }
protected: protected:
void setupFilters();
GLuint internalLoadGLTexture(uchar* pixels, int channels, int w, int h); GLuint internalLoadGLTexture(uchar* pixels, int channels, int w, int h);
GLuint m_textureId; GLuint m_textureId;
Size m_size; Size m_size;
Boolean<false> m_useMipmaps;
Boolean<false> m_smooth; Boolean<false> m_smooth;
}; };

@ -154,7 +154,7 @@ void Application::registerLuaFunctions()
g_lua.bindClassMemberFunction<UIWidget>("setWidth", &UIWidget::setWidth); g_lua.bindClassMemberFunction<UIWidget>("setWidth", &UIWidget::setWidth);
g_lua.bindClassMemberFunction<UIWidget>("setHeight", &UIWidget::setHeight); g_lua.bindClassMemberFunction<UIWidget>("setHeight", &UIWidget::setHeight);
g_lua.bindClassMemberFunction<UIWidget>("setSize", &UIWidget::setSize); g_lua.bindClassMemberFunction<UIWidget>("setSize", &UIWidget::setSize);
g_lua.bindClassMemberFunction<UIWidget>("setPos", &UIWidget::setPos); g_lua.bindClassMemberFunction<UIWidget>("setPosition", &UIWidget::setPosition);
g_lua.bindClassMemberFunction<UIWidget>("setColor", &UIWidget::setColor); g_lua.bindClassMemberFunction<UIWidget>("setColor", &UIWidget::setColor);
g_lua.bindClassMemberFunction<UIWidget>("setBackgroundColor", &UIWidget::setBackgroundColor); g_lua.bindClassMemberFunction<UIWidget>("setBackgroundColor", &UIWidget::setBackgroundColor);
g_lua.bindClassMemberFunction<UIWidget>("setBackgroundOffsetX", &UIWidget::setBackgroundOffsetX); g_lua.bindClassMemberFunction<UIWidget>("setBackgroundOffsetX", &UIWidget::setBackgroundOffsetX);
@ -192,7 +192,7 @@ void Application::registerLuaFunctions()
g_lua.bindClassMemberFunction<UIWidget>("setOpacity", &UIWidget::setOpacity); g_lua.bindClassMemberFunction<UIWidget>("setOpacity", &UIWidget::setOpacity);
g_lua.bindClassMemberFunction<UIWidget>("getX", &UIWidget::getX); g_lua.bindClassMemberFunction<UIWidget>("getX", &UIWidget::getX);
g_lua.bindClassMemberFunction<UIWidget>("getY", &UIWidget::getY); g_lua.bindClassMemberFunction<UIWidget>("getY", &UIWidget::getY);
g_lua.bindClassMemberFunction<UIWidget>("getPos", &UIWidget::getPos); g_lua.bindClassMemberFunction<UIWidget>("getPosition", &UIWidget::getPosition);
g_lua.bindClassMemberFunction<UIWidget>("getWidth", &UIWidget::getWidth); g_lua.bindClassMemberFunction<UIWidget>("getWidth", &UIWidget::getWidth);
g_lua.bindClassMemberFunction<UIWidget>("getHeight", &UIWidget::getHeight); g_lua.bindClassMemberFunction<UIWidget>("getHeight", &UIWidget::getHeight);
g_lua.bindClassMemberFunction<UIWidget>("getSize", &UIWidget::getSize); g_lua.bindClassMemberFunction<UIWidget>("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", "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", "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", "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 // PlatformWindow
g_lua.registerStaticClass("g_window"); 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", "getWidth", std::bind(&PlatformWindow::getWidth, &g_window));
g_lua.bindClassStaticFunction("g_window", "getHeight", std::bind(&PlatformWindow::getHeight, &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", "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", "getX", std::bind(&PlatformWindow::getX, &g_window));
g_lua.bindClassStaticFunction("g_window", "getY", std::bind(&PlatformWindow::getY, &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)); g_lua.bindClassStaticFunction("g_window", "getMousePos", std::bind(&PlatformWindow::getMousePos, &g_window));

@ -48,7 +48,8 @@ void LuaInterface::init()
// register LuaObject, the base of all other objects // register LuaObject, the base of all other objects
registerClass<LuaObject>(); registerClass<LuaObject>();
bindClassMemberGetField<LuaObject>("use_count", &LuaObject::getUseCount); bindClassMemberFunction<LuaObject>("getUseCount", &LuaObject::getUseCount);
bindClassMemberFunction<LuaObject>("getClassName", &LuaObject::getClassName);
} }
void LuaInterface::terminate() void LuaInterface::terminate()
@ -958,7 +959,7 @@ void LuaInterface::pushObject(const LuaObjectPtr& obj)
new(newUserdata(sizeof(LuaObjectPtr))) LuaObjectPtr(obj); new(newUserdata(sizeof(LuaObjectPtr))) LuaObjectPtr(obj);
// set the userdata metatable // set the userdata metatable
getGlobal(Fw::mkstr(obj->getLuaObjectName(), "_mt")); getGlobal(Fw::mkstr(obj->getClassName(), "_mt"));
assert(!isNil()); assert(!isNil());
setMetatable(); setMetatable();
} }

@ -61,8 +61,8 @@ public:
/// @note each userdata of this object on lua counts as a reference /// @note each userdata of this object on lua counts as a reference
int getUseCount(); int getUseCount();
/// Returns the class name used in Lua /// Returns the derived class name, its the same name used in Lua
virtual std::string getLuaObjectName() const { virtual std::string getClassName() const {
// TODO: this could be cached for more performance // TODO: this could be cached for more performance
return Fw::demangleName(typeid(*this).name()); return Fw::demangleName(typeid(*this).name());
} }

@ -215,7 +215,11 @@ bool luavalue_cast(int index, Size& size)
void push_otml_subnode_luavalue(const OTMLNodePtr& node) void push_otml_subnode_luavalue(const OTMLNodePtr& node)
{ {
if(node->hasValue()) { if(node->hasValue()) {
g_lua.pushString(node->value()); // convert boolean types
if(node->value() == "true" || node->value() == "false")
g_lua.pushBoolean(node->value<bool>());
else
g_lua.pushString(node->value());
} else if(node->hasChildren()) { } else if(node->hasChildren()) {
g_lua.newTable(); g_lua.newTable();
bool pushedChild = false; bool pushedChild = false;
@ -225,6 +229,7 @@ void push_otml_subnode_luavalue(const OTMLNodePtr& node)
if(!g_lua.isNil()) { if(!g_lua.isNil()) {
if(cnode->isUnique()) { if(cnode->isUnique()) {
g_lua.pushString(cnode->tag()); g_lua.pushString(cnode->tag());
g_lua.insert(-2);
g_lua.rawSet(); g_lua.rawSet();
} else } else
g_lua.rawSeti(currentIndex++); g_lua.rawSeti(currentIndex++);
@ -242,16 +247,18 @@ void push_otml_subnode_luavalue(const OTMLNodePtr& node)
void push_luavalue(const OTMLNodePtr& node) void push_luavalue(const OTMLNodePtr& node)
{ {
g_lua.newTable(); if(node) {
for(const OTMLNodePtr& cnode : node->children()) { g_lua.newTable();
if(cnode->isUnique()) { int currentIndex = 1;
for(const OTMLNodePtr& cnode : node->children()) {
push_otml_subnode_luavalue(cnode); push_otml_subnode_luavalue(cnode);
if(!g_lua.isNil()) { if(cnode->isUnique()) {
g_lua.setField(cnode->tag()); g_lua.setField(cnode->tag());
} else } else
g_lua.pop(); g_lua.rawSeti(currentIndex++);
} }
} } else
g_lua.pushNil();
} }
bool luavalue_cast(int index, OTMLNodePtr& node) bool luavalue_cast(int index, OTMLNodePtr& node)
@ -261,15 +268,29 @@ bool luavalue_cast(int index, OTMLNodePtr& node)
if(g_lua.isTable(index)) { if(g_lua.isTable(index)) {
g_lua.pushNil(); g_lua.pushNil();
while(g_lua.next(index < 0 ? index-1 : index)) { 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()) { if(g_lua.isTable()) {
OTMLNodePtr cnode; OTMLNodePtr cnode;
if(luavalue_cast(-1, node)) { if(luavalue_cast(-1, cnode)) {
cnode->setTag(cnodeName); if(cnodeName.empty())
node->setUnique(false);
else
cnode->setTag(cnodeName);
node->addChild(cnode); node->addChild(cnode);
} }
} else } else {
node->writeAt(cnodeName, g_lua.toString()); std::string value;
if(g_lua.isBoolean())
value = Fw::unsafeCast<std::string>(g_lua.toBoolean());
else
value = g_lua.toString();
if(cnodeName.empty())
node->writeIn(value);
else
node->writeAt(cnodeName, value);
}
g_lua.pop(); g_lua.pop();
} }
return true; return true;

@ -56,10 +56,10 @@ public:
TPoint<T>& operator+=(T other) { x+=other; y+=other; return *this; } TPoint<T>& operator+=(T other) { x+=other; y+=other; return *this; }
TPoint<T> operator-(T other) const { return TPoint<T>(x - other, y - other); } TPoint<T> operator-(T other) const { return TPoint<T>(x - other, y - other); }
TPoint<T>& operator-=(T other) { x-=other; y-=other; return *this; } TPoint<T>& operator-=(T other) { x-=other; y-=other; return *this; }
TPoint<T> operator*(const T v) const { return TPoint<T>(x*v, y*v); } TPoint<T> operator*(float v) const { return TPoint<T>(x*v, y*v); }
TPoint<T>& operator*=(const T v) { x*=v; y*=v; return *this; } TPoint<T>& operator*=(float v) { x*=v; y*=v; return *this; }
TPoint<T> operator/(const T v) const { return TPoint<T>(x/v, y/v); } TPoint<T> operator/(float v) const { return TPoint<T>(x/v, y/v); }
TPoint<T>& operator/=(const T v) { x/=v; y/=v; return *this; } TPoint<T>& operator/=(float v) { x/=v; y/=v; return *this; }
bool operator<=(const TPoint<T>&other) const { return x<=other.x && y<=other.y; } bool operator<=(const TPoint<T>&other) const { return x<=other.x && y<=other.y; }
bool operator>=(const TPoint<T>&other) const { return x>=other.x && y>=other.y; } bool operator>=(const TPoint<T>&other) const { return x>=other.x && y>=other.y; }

@ -52,15 +52,19 @@ public:
TSize<T>& operator+=(const TSize<T>& other) { wd+=other.wd; ht+=other.ht; return *this; } TSize<T>& operator+=(const TSize<T>& other) { wd+=other.wd; ht+=other.ht; return *this; }
TSize<T> operator-(const TSize<T>& other) const { return TSize<T>(wd - other.wd, ht - other.ht); } TSize<T> operator-(const TSize<T>& other) const { return TSize<T>(wd - other.wd, ht - other.ht); }
TSize<T>& operator-=(const TSize<T>& other) { wd-=other.wd; ht-=other.ht; return *this; } TSize<T>& operator-=(const TSize<T>& other) { wd-=other.wd; ht-=other.ht; return *this; }
TSize<T> operator*(const TSize<T>& other) const { return TSize<T>((T)other.wd*wd, (T)ht*other.ht); }
TSize<T>& operator*=(const TSize<T>& other) { wd=(T)other.wd*wd; ht=(T)ht*other.ht; return *this; }
TSize<T> operator/(const TSize<T>& other) const { return TSize<T>((T)wd/other.wd, (T)ht/other.ht); }
TSize<T>& operator/=(const TSize<T>& other) { (T)wd/=other.wd; (T)ht/=other.ht; return *this; }
TSize<T> operator*(const float v) const { return TSize<T>((T)v*wd, (T)ht*v); } TSize<T> operator*(const float v) const { return TSize<T>((T)v*wd, (T)ht*v); }
TSize<T>& operator*=(const float v) { wd=(T)v*wd; ht=(T)ht*v; return *this; } TSize<T>& operator*=(const float v) { wd=(T)v*wd; ht=(T)ht*v; return *this; }
TSize<T> operator/(const float v) const { return TSize<T>((T)wd/v, (T)ht/v); } TSize<T> operator/(const float v) const { return TSize<T>((T)wd/v, (T)ht/v); }
TSize<T>& operator/=(const float v) { (T)wd/=v; (T)ht/=v; return *this; } TSize<T>& operator/=(const float v) { wd/=v; ht/=v; return *this; }
bool operator<=(const TSize<T>&other) const { return wd<=other.wd || ht<=other.ht; } bool operator<=(const TSize<T>&other) const { return wd<=other.wd && ht<=other.ht; }
bool operator>=(const TSize<T>&other) const { return wd>=other.wd || ht>=other.ht; } bool operator>=(const TSize<T>&other) const { return wd>=other.wd && ht>=other.ht; }
bool operator<(const TSize<T>&other) const { return wd<other.wd || ht<other.ht; } bool operator<(const TSize<T>&other) const { return wd<other.wd && ht<other.ht; }
bool operator>(const TSize<T>&other) const { return wd>other.wd || ht>other.ht; } bool operator>(const TSize<T>&other) const { return wd>other.wd && ht>other.ht; }
TSize<T>& operator=(const TSize<T>& other) { wd = other.wd; ht = other.ht; return *this; } TSize<T>& operator=(const TSize<T>& other) { wd = other.wd; ht = other.ht; return *this; }
bool operator==(const TSize<T>& other) const { return other.wd==wd && other.ht==ht; } bool operator==(const TSize<T>& other) const { return other.wd==wd && other.ht==ht; }

@ -35,6 +35,7 @@ Connection::Connection() :
{ {
m_connected = false; m_connected = false;
m_connecting = false; m_connecting = false;
m_sendBufferSize = 0;
} }
Connection::~Connection() Connection::~Connection()
@ -57,6 +58,7 @@ void Connection::connect(const std::string& host, uint16 port, const SimpleCallb
{ {
m_connected = false; m_connected = false;
m_connecting = true; m_connecting = true;
m_error.clear();
m_connectCallback = connectCallback; m_connectCallback = connectCallback;
asio::ip::tcp::resolver::query query(host, Fw::unsafeCast<std::string>(port)); asio::ip::tcp::resolver::query query(host, Fw::unsafeCast<std::string>(port));
@ -85,6 +87,10 @@ void Connection::close()
if(!m_connected && !m_connecting) if(!m_connected && !m_connecting)
return; 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_connecting = false;
m_connected = false; m_connected = false;
m_connectCallback = nullptr; m_connectCallback = nullptr;
@ -104,17 +110,34 @@ void Connection::close()
void Connection::write(uint8* buffer, uint16 size) void Connection::write(uint8* buffer, uint16 size)
{ {
m_writeTimer.cancel();
if(!m_connected) if(!m_connected)
return; return;
asio::async_write(m_socket, // send old buffer if we can't add more data
asio::buffer(buffer, size), if(m_sendBufferSize + size >= SEND_BUFFER_SIZE && m_sendEvent)
std::bind(&Connection::onWrite, shared_from_this(), _1, _2)); 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)); if(!m_sendEvent || m_sendEvent->isExecuted() || m_sendEvent->isCanceled()) {
m_writeTimer.async_wait(std::bind(&Connection::onTimeout, shared_from_this(), _1)); 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) void Connection::read(uint16 bytes, const RecvCallback& callback)
@ -157,7 +180,7 @@ void Connection::onConnect(const boost::system::error_code& error)
if(!error) { if(!error) {
m_connected = true; 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); boost::asio::ip::tcp::no_delay option(true);
m_socket.set_option(option); 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) void Connection::handleError(const boost::system::error_code& error)
{ {
if(error != asio::error::operation_aborted) { if(error != asio::error::operation_aborted) {
m_error = error;
if(m_errorCallback) if(m_errorCallback)
m_errorCallback(error); m_errorCallback(error);
if(m_connected || m_connecting) if(m_connected || m_connecting)

@ -25,6 +25,8 @@
#include "declarations.h" #include "declarations.h"
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <framework/core/timer.h>
#include <framework/core/declarations.h>
class Connection : public std::enable_shared_from_this<Connection>, boost::noncopyable class Connection : public std::enable_shared_from_this<Connection>, boost::noncopyable
{ {
@ -34,6 +36,8 @@ class Connection : public std::enable_shared_from_this<Connection>, boost::nonco
enum { enum {
READ_TIMEOUT = 30, READ_TIMEOUT = 30,
WRITE_TIMEOUT = 30, WRITE_TIMEOUT = 30,
SEND_INTERVAL = 10,
SEND_BUFFER_SIZE = 65536,
RECV_BUFFER_SIZE = 65536 RECV_BUFFER_SIZE = 65536
}; };
@ -53,6 +57,7 @@ public:
void setErrorCallback(const ErrorCallback& errorCallback) { m_errorCallback = errorCallback; } void setErrorCallback(const ErrorCallback& errorCallback) { m_errorCallback = errorCallback; }
boost::system::error_code getError() const { return m_error; }
bool isConnecting() const { return m_connecting; } bool isConnecting() const { return m_connecting; }
bool isConnected() const { return m_connected; } bool isConnected() const { return m_connected; }
@ -72,9 +77,14 @@ protected:
asio::ip::tcp::resolver m_resolver; asio::ip::tcp::resolver m_resolver;
asio::ip::tcp::socket m_socket; asio::ip::tcp::socket m_socket;
uint8 m_sendBuffer[SEND_BUFFER_SIZE];
uint8 m_recvBuffer[RECV_BUFFER_SIZE]; uint8 m_recvBuffer[RECV_BUFFER_SIZE];
bool m_connected; bool m_connected;
bool m_connecting; bool m_connecting;
boost::system::error_code m_error;
int m_sendBufferSize;
Timer m_sendTimer;
ScheduledEventPtr m_sendEvent;
friend class Server; friend class Server;
}; };

@ -147,6 +147,7 @@ void OTMLNode::write(const T& v) {
template<typename T> template<typename T>
void OTMLNode::writeAt(const std::string& childTag, const T& v) { void OTMLNode::writeAt(const std::string& childTag, const T& v) {
OTMLNodePtr child = OTMLNode::create(childTag); OTMLNodePtr child = OTMLNode::create(childTag);
child->setUnique(true);
child->write<T>(v); child->write<T>(v);
addChild(child); addChild(child);
} }

@ -38,7 +38,7 @@ PlatformWindow& g_window = window;
void PlatformWindow::updateUnmaximizedCoords() void PlatformWindow::updateUnmaximizedCoords()
{ {
if(!isMaximized() && !isFullscreen()) { if(!isMaximized() && !isFullscreen()) {
m_unmaximizedPos = m_pos; m_unmaximizedPos = m_position;
m_unmaximizedSize = m_size; m_unmaximizedSize = m_size;
} }
} }

@ -73,9 +73,9 @@ public:
int getWidth() { return m_size.width(); } int getWidth() { return m_size.width(); }
int getHeight() { return m_size.height(); } int getHeight() { return m_size.height(); }
Point getUnmaximizedPos() { return m_unmaximizedPos; } Point getUnmaximizedPos() { return m_unmaximizedPos; }
Point getPos() { return m_pos; } Point getPosition() { return m_position; }
int getX() { return m_pos.x; } int getX() { return m_position.x; }
int getY() { return m_pos.y; } int getY() { return m_position.y; }
Point getMousePos() { return m_inputEvent.mousePos; } Point getMousePos() { return m_inputEvent.mousePos; }
int getKeyboardModifiers() { return m_inputEvent.keyboardModifiers; } int getKeyboardModifiers() { return m_inputEvent.keyboardModifiers; }
bool isKeyPressed(Fw::Key keyCode) { return m_keysState[keyCode]; } bool isKeyPressed(Fw::Key keyCode) { return m_keysState[keyCode]; }
@ -104,7 +104,7 @@ protected:
Timer m_keyPressTimer; Timer m_keyPressTimer;
Size m_size; Size m_size;
Point m_pos; Point m_position;
Size m_unmaximizedSize; Size m_unmaximizedSize;
Point m_unmaximizedPos; Point m_unmaximizedPos;
InputEvent m_inputEvent; InputEvent m_inputEvent;

@ -278,7 +278,7 @@ void WIN32Window::internalCreateWindow()
DWORD dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; DWORD dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
DWORD dwStyle = WS_OVERLAPPEDWINDOW; 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); AdjustWindowRectEx(&windowRect, dwStyle, FALSE, dwExStyle);
updateUnmaximizedCoords(); updateUnmaximizedCoords();
@ -361,14 +361,14 @@ void *WIN32Window::getExtensionProcAddress(const char *ext)
void WIN32Window::move(const Point& pos) 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); 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); MoveWindow(m_window, windowRect.left, windowRect.top, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, TRUE);
} }
void WIN32Window::resize(const Size& size) 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); 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); 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; break;
} }
case WM_MOVE: { case WM_MOVE: {
m_pos.x = LOWORD(lParam); m_position.x = LOWORD(lParam);
m_pos.y = HIWORD(lParam); m_position.y = HIWORD(lParam);
break; break;
} }
case WM_SIZE: { case WM_SIZE: {

@ -284,7 +284,7 @@ void X11Window::internalCreateWindow()
updateUnmaximizedCoords(); updateUnmaximizedCoords();
m_window = XCreateWindow(m_display, m_rootWindow, 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, 0,
depth, depth,
InputOutput, InputOutput,
@ -300,7 +300,7 @@ void X11Window::internalCreateWindow()
XSetWMHints(m_display, m_window, &hints); XSetWMHints(m_display, m_window, &hints);
// ensure window position // 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 // handle wm_delete events
m_wmDelete = XInternAtom(m_display, "WM_DELETE_WINDOW", True); m_wmDelete = XInternAtom(m_display, "WM_DELETE_WINDOW", True);
@ -580,7 +580,7 @@ void X11Window::poll()
} }
// updates window pos // updates window pos
m_pos = newPos; m_position = newPos;
updateUnmaximizedCoords(); updateUnmaximizedCoords();
break; break;
} }

@ -90,7 +90,7 @@ void UIWidget::drawChildren()
g_painter.setColor(Fw::green); g_painter.setColor(Fw::green);
g_painter.drawBoundingRect(child->getRect()); 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); g_painter.setOpacity(oldOpacity);
} }

@ -189,7 +189,7 @@ protected:
// function shortcuts // function shortcuts
public: 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 move(int x, int y) { setRect(Rect(x, y, getSize())); }
void hide() { setVisible(false); } void hide() { setVisible(false); }
void show() { setVisible(true); } void show() { setVisible(true); }
@ -262,7 +262,7 @@ public:
void setWidth(int width) { resize(width, getHeight()); } void setWidth(int width) { resize(width, getHeight()); }
void setHeight(int height) { resize(getWidth(), height); } void setHeight(int height) { resize(getWidth(), height); }
void setSize(const Size& size) { resize(size.width(), size.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 setColor(const Color& color) { m_color = color; }
void setBackgroundColor(const Color& color) { m_backgroundColor = color; } void setBackgroundColor(const Color& color) { m_backgroundColor = color; }
void setBackgroundOffsetX(int x) { m_backgroundRect.setX(x); } void setBackgroundOffsetX(int x) { m_backgroundRect.setX(x); }
@ -309,7 +309,7 @@ public:
int getX() { return m_rect.x(); } int getX() { return m_rect.x(); }
int getY() { return m_rect.y(); } 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 getWidth() { return m_rect.width(); }
int getHeight() { return m_rect.height(); } int getHeight() { return m_rect.height(); }
Size getSize() { return m_rect.size(); } Size getSize() { return m_rect.size(); }

@ -55,7 +55,7 @@ void UIWidget::parseBaseStyle(const OTMLNodePtr& styleNode)
else if(node->tag() == "y") else if(node->tag() == "y")
setY(node->value<int>()); setY(node->value<int>());
else if(node->tag() == "pos") else if(node->tag() == "pos")
setPos(node->value<Point>()); setPosition(node->value<Point>());
else if(node->tag() == "width") else if(node->tag() == "width")
setWidth(node->value<int>()); setWidth(node->value<int>());
else if(node->tag() == "height") else if(node->tag() == "height")

@ -31,5 +31,3 @@ int main(int argc, const char* argv[])
app.terminate(); app.terminate();
return 0; return 0;
} }
// for freenode: fk39HHDJJF

@ -27,6 +27,7 @@ SET(otclient_SOURCES ${otclient_SOURCES}
# otclient core # otclient core
${CMAKE_CURRENT_LIST_DIR}/core/game.cpp ${CMAKE_CURRENT_LIST_DIR}/core/game.cpp
${CMAKE_CURRENT_LIST_DIR}/core/map.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/thingstype.cpp
${CMAKE_CURRENT_LIST_DIR}/core/spritemanager.cpp ${CMAKE_CURRENT_LIST_DIR}/core/spritemanager.cpp
${CMAKE_CURRENT_LIST_DIR}/core/item.cpp ${CMAKE_CURRENT_LIST_DIR}/core/item.cpp

@ -31,6 +31,51 @@ namespace Otc
static const char* AppCompactName = "otclient"; static const char* AppCompactName = "otclient";
static const char* AppVersion = "0.4.0"; 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 { enum DatOpts {
DatGround = 0, DatGround = 0,
DatGroundClip, DatGroundClip,
@ -219,6 +264,23 @@ namespace Otc
IconPzBlock = 8192, IconPzBlock = 8192,
IconPz = 16384 IconPz = 16384
}; };
enum SpeakType {
SpeakSay = 1,
SpeakWhisper,
SpeakYell,
SpeakBroadcast,
SpeakPrivate,
SpeakPrivateRed,
SpeakPrivatePlayerToNpc,
SpeakPrivateNpcToPlayer,
SpeakChannelYellow,
SpeakChannelWhite,
SpeakChannelRed,
SpeakChannelOrange,
SpeakMonsterSay,
SpeakMonsterYell
};
} }
#endif #endif

@ -31,25 +31,26 @@ AnimatedText::AnimatedText()
m_font = g_fonts.getFont("verdana-11px-rounded"); 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(); if(visibleRect.contains(rect)) {
//TODO: cache into a framebuffer
// schedule removal m_font->renderText(m_text, rect, Fw::AlignLeft, m_color);
g_dispatcher.scheduleEvent([self]() { }
g_map.removeThing(self);
}, DURATION);
} }
void AnimatedText::draw(const Point& p, const Rect& visibleRect) void AnimatedText::startAnimation()
{ {
if(m_font) { m_animationTimer.restart();
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)) // schedule removal
m_font->renderText(m_text, rect, Fw::AlignLeft, m_color); auto self = asAnimatedText();
} g_dispatcher.scheduleEvent([self]() { g_map.removeThing(self); }, Otc::ANIMATED_TEXT_DURATION);
} }
void AnimatedText::setColor(int color) void AnimatedText::setColor(int color)
@ -59,7 +60,6 @@ void AnimatedText::setColor(int color)
void AnimatedText::setText(const std::string& text) 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; m_text = text;
} }

@ -25,18 +25,15 @@
#include "thing.h" #include "thing.h"
#include <framework/graphics/fontmanager.h> #include <framework/graphics/fontmanager.h>
#include <framework/core/timer.h>
class AnimatedText : public Thing class AnimatedText : public Thing
{ {
public: public:
enum {
DURATION = 1000
};
AnimatedText(); AnimatedText();
void start(); void draw(const Point& dest, const Rect& visibleRect);
void draw(const Point& p, const Rect& visibleRect); void startAnimation();
void setColor(int color); void setColor(int color);
void setText(const std::string& text); void setText(const std::string& text);
@ -48,7 +45,7 @@ private:
Size m_textSize; Size m_textSize;
std::string m_text; std::string m_text;
Color m_color; Color m_color;
double m_startTime; Timer m_animationTimer;
}; };
#endif #endif

@ -44,6 +44,7 @@ Creature::Creature() : Thing()
m_showVolatileSquare = false; m_showVolatileSquare = false;
m_showStaticSquare = false; m_showStaticSquare = false;
m_direction = Otc::South; m_direction = Otc::South;
m_walkAnimationPhase = 0;
m_walking = false; m_walking = false;
m_walkInterval = 0; m_walkInterval = 0;
m_walkAnimationInterval = 0; m_walkAnimationInterval = 0;
@ -63,16 +64,18 @@ int LEGS_COLOR_UNIFORM = 12;
int FEET_COLOR_UNIFORM = 13; int FEET_COLOR_UNIFORM = 13;
int MASK_TEXTURE_UNIFORM = 14; 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.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.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); g_painter.setColor(Fw::white);
@ -88,12 +91,29 @@ void Creature::draw(const Point& p, const Rect&)
outfitProgram->bindUniformLocation(MASK_TEXTURE_UNIFORM, "maskTexture"); 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) { 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. // 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; continue;
g_painter.setCustomProgram(outfitProgram); 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(LEGS_COLOR_UNIFORM, m_outfit.getLegsColor());
outfitProgram->setUniformValue(FEET_COLOR_UNIFORM, m_outfit.getFeetColor()); outfitProgram->setUniformValue(FEET_COLOR_UNIFORM, m_outfit.getFeetColor());
for(int h = 0; h < m_type->dimensions[ThingType::Height]; h++) { for(int h = 0; h < getDimensionHeight(); h++) {
for(int w = 0; w < m_type->dimensions[ThingType::Width]; w++) { for(int w = 0; w < getDimensionWidth(); w++) {
int spriteId = m_type->getSpriteId(w, h, 0, m_xPattern, m_yPattern, m_zPattern, m_animation); // setup texture outfit mask
if(!spriteId) TexturePtr maskTex;
continue; if(getLayers() > 1) {
TexturePtr spriteTex = g_sprites.getSpriteTexture(spriteId); int maskId = getSpriteId(w, h, 1, xPattern, yPattern, zPattern, m_walkAnimationPhase);
if(!spriteTex) maskTex = g_sprites.getSpriteTexture(maskId);
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);
} }
outfitProgram->setUniformTexture(MASK_TEXTURE_UNIFORM, maskTex, 1);
Rect drawRect(((p + m_walkOffset).x - w*32) - m_type->parameters[ThingType::DisplacementX], internalDraw(dest + (animationOffset - Point(w,h)*Otc::TILE_PIXELS)*scaleFactor,
((p + m_walkOffset).y - h*32) - m_type->parameters[ThingType::DisplacementY], scaleFactor, w, h, xPattern, yPattern, zPattern, 0, animationPhase);
32, 32);
g_painter.drawTexturedRect(drawRect, spriteTex);
} }
} }
g_painter.releaseCustomProgram(); 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); 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; fillColor = m_informationColor;
// calculate main rects // calculate main rects
Rect backgroundRect = Rect(x-(13.5), y, 27, 4); Rect backgroundRect = Rect(point.x-(13.5), point.y, 27, 4);
backgroundRect.bind(visibleRect); backgroundRect.bind(parentRect);
Rect textRect = Rect(x - m_nameSize.width() / 2.0, y-12, m_nameSize); Rect textRect = Rect(point.x - m_nameSize.width() / 2.0, point.y-12, m_nameSize);
textRect.bind(visibleRect); textRect.bind(parentRect);
// distance them // distance them
if(textRect.top() == visibleRect.top()) if(textRect.top() == parentRect.top())
backgroundRect.moveTop(textRect.top() + 12); backgroundRect.moveTop(textRect.top() + 12);
if(backgroundRect.bottom() == visibleRect.bottom()) if(backgroundRect.bottom() == parentRect.bottom())
textRect.moveTop(backgroundRect.top() - 12); textRect.moveTop(backgroundRect.top() - 12);
// health rect is based on background rect, so no worries // 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) { if(m_skull != Otc::SkullNone && m_skullTexture) {
g_painter.setColor(Fw::white); 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) { if(m_shield != Otc::ShieldNone && m_shieldTexture && m_showShieldTexture) {
g_painter.setColor(Fw::white); 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) { if(m_emblem != Otc::EmblemNone && m_emblemTexture) {
g_painter.setColor(Fw::white); 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 // calculates walk interval
float interval = 1000; 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) if(groundSpeed != 0)
interval = (1000.0f * groundSpeed) / m_speed; interval = (1000.0f * groundSpeed) / m_speed;
@ -236,7 +272,7 @@ void Creature::stopWalk()
// reset walk animation states // reset walk animation states
m_walkOffset = Point(0,0); m_walkOffset = Point(0,0);
m_animation = 0; m_walkAnimationPhase = 0;
// stops the walk right away // stops the walk right away
terminateWalk(); terminateWalk();
@ -248,10 +284,10 @@ void Creature::updateWalkAnimation(int totalPixelsWalked)
if(m_outfit.getCategory() != ThingsType::Creature) if(m_outfit.getCategory() != ThingsType::Creature)
return; return;
if(totalPixelsWalked == 32 || totalPixelsWalked == 0 || m_type->dimensions[ThingType::AnimationPhases] <= 1) if(totalPixelsWalked == 32 || totalPixelsWalked == 0 || getAnimationPhases() <= 1)
m_animation = 0; m_walkAnimationPhase = 0;
else if(m_type->dimensions[ThingType::AnimationPhases] > 1) else if(getAnimationPhases() > 1)
m_animation = 1 + ((totalPixelsWalked * 4) / Map::NUM_TILE_PIXELS) % (m_type->dimensions[ThingType::AnimationPhases] - 1); m_walkAnimationPhase = 1 + ((totalPixelsWalked * 4) / Otc::TILE_PIXELS) % (getAnimationPhases() - 1);
} }
void Creature::updateWalkOffset(int totalPixelsWalked) void Creature::updateWalkOffset(int totalPixelsWalked)
@ -268,6 +304,36 @@ void Creature::updateWalkOffset(int totalPixelsWalked)
m_walkOffset.x = 32 - 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() void Creature::nextWalkUpdate()
{ {
// remove any previous scheduled walk updates // remove any previous scheduled walk updates
@ -295,6 +361,7 @@ void Creature::updateWalk()
// update walk animation and offsets // update walk animation and offsets
updateWalkAnimation(totalPixelsWalked); updateWalkAnimation(totalPixelsWalked);
updateWalkOffset(totalPixelsWalked); updateWalkOffset(totalPixelsWalked);
updateWalkingTile();
// terminate walk // terminate walk
if(m_walking && m_walkTimer.ticksElapsed() >= m_walkInterval) if(m_walking && m_walkTimer.ticksElapsed() >= m_walkInterval)
@ -315,6 +382,11 @@ void Creature::terminateWalk()
m_walkTurnDirection = Otc::InvalidDirection; m_walkTurnDirection = Otc::InvalidDirection;
} }
if(m_walkingTile) {
m_walkingTile->removeWalkingCreature(asCreature());
m_walkingTile = nullptr;
}
m_walking = false; m_walking = false;
} }
@ -358,40 +430,13 @@ void Creature::setHealthPercent(uint8 healthPercent)
void Creature::setDirection(Otc::Direction direction) 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; m_direction = direction;
} }
void Creature::setOutfit(const Outfit& outfit) void Creature::setOutfit(const Outfit& outfit)
{ {
m_outfit = outfit; m_outfit = outfit;
m_type = getType(); m_type = g_thingsType.getThingType(outfit.getId(), outfit.getCategory());
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();
}
} }
void Creature::setSkull(uint8 skull) void Creature::setSkull(uint8 skull)
@ -449,26 +494,6 @@ void Creature::addVolatileSquare(uint8 color)
}, VOLATILE_SQUARE_DURATION); }, 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() void Creature::updateShield()
{ {
m_showShieldTexture = !m_showShieldTexture; m_showShieldTexture = !m_showShieldTexture;
@ -483,8 +508,17 @@ void Creature::updateShield()
m_showShieldTexture = true; 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;
} }

@ -34,16 +34,16 @@ class Creature : public Thing
public: public:
enum { enum {
SHIELD_BLINK_TICKS = 500, SHIELD_BLINK_TICKS = 500,
INVISIBLE_TICKS = 500,
VOLATILE_SQUARE_DURATION = 1000 VOLATILE_SQUARE_DURATION = 1000
}; };
Creature(); Creature();
virtual ~Creature() { } virtual ~Creature() { }
virtual void draw(const Point& p, const Rect&); virtual void draw(const Point& dest, float scaleFactor, bool animate);
void drawInformation(int x, int y, bool useGray, const Rect& visibleRect); void drawInformation(const Point& point, bool useGray, const Rect& parentRect);
void setId(uint32 id) { m_id = id; }
void setName(const std::string& name); void setName(const std::string& name);
void setHealthPercent(uint8 healthPercent); void setHealthPercent(uint8 healthPercent);
void setDirection(Otc::Direction direction); void setDirection(Otc::Direction direction);
@ -64,6 +64,7 @@ public:
void showStaticSquare(const Color& color) { m_showStaticSquare = true; m_staticSquareColor = color; } void showStaticSquare(const Color& color) { m_showStaticSquare = true; m_staticSquareColor = color; }
void hideStaticSquare() { m_showStaticSquare = false; } void hideStaticSquare() { m_showStaticSquare = false; }
uint32 getId() { return m_id; }
std::string getName() { return m_name; } std::string getName() { return m_name; }
uint8 getHealthPercent() { return m_healthPercent; } uint8 getHealthPercent() { return m_healthPercent; }
Otc::Direction getDirection() { return m_direction; } Otc::Direction getDirection() { return m_direction; }
@ -74,17 +75,15 @@ public:
uint8 getShield() { return m_shield; } uint8 getShield() { return m_shield; }
uint8 getEmblem() { return m_emblem; } uint8 getEmblem() { return m_emblem; }
bool getPassable() { return m_passable; } bool getPassable() { return m_passable; }
Point getDrawOffset();
Point getWalkOffset() { return m_walkOffset; }
void updateInvisibleAnimation();
void updateShield(); void updateShield();
ThingType *getType();
// walk related // walk related
void turn(Otc::Direction direction); void turn(Otc::Direction direction);
virtual void walk(const Position& oldPos, const Position& newPos); virtual void walk(const Position& oldPos, const Position& newPos);
virtual void stopWalk(); virtual void stopWalk();
Point getWalkOffset() { return m_walkOffset; }
bool isWalking() { return m_walking; } bool isWalking() { return m_walking; }
@ -93,10 +92,12 @@ public:
protected: protected:
virtual void updateWalkAnimation(int totalPixelsWalked); virtual void updateWalkAnimation(int totalPixelsWalked);
virtual void updateWalkOffset(int totalPixelsWalked); virtual void updateWalkOffset(int totalPixelsWalked);
void updateWalkingTile();
virtual void nextWalkUpdate(); virtual void nextWalkUpdate();
virtual void updateWalk(); virtual void updateWalk();
virtual void terminateWalk(); virtual void terminateWalk();
uint32 m_id;
std::string m_name; std::string m_name;
Size m_nameSize; Size m_nameSize;
uint8 m_healthPercent; uint8 m_healthPercent;
@ -115,7 +116,9 @@ protected:
Color m_informationColor; Color m_informationColor;
// walk related // walk related
int m_walkAnimationPhase;
Timer m_walkTimer; Timer m_walkTimer;
TilePtr m_walkingTile;
int m_walkInterval; int m_walkInterval;
int m_walkAnimationInterval; int m_walkAnimationInterval;
bool m_walking; bool m_walking;

@ -25,6 +25,8 @@
#include <otclient/global.h> #include <otclient/global.h>
class Map;
class MapView;
class Tile; class Tile;
class Thing; class Thing;
class Item; class Item;
@ -38,6 +40,7 @@ class Missile;
class AnimatedText; class AnimatedText;
class StaticText; class StaticText;
typedef std::shared_ptr<MapView> MapViewPtr;
typedef std::shared_ptr<Tile> TilePtr; typedef std::shared_ptr<Tile> TilePtr;
typedef std::shared_ptr<Thing> ThingPtr; typedef std::shared_ptr<Thing> ThingPtr;
typedef std::shared_ptr<Item> ItemPtr; typedef std::shared_ptr<Item> ItemPtr;

@ -27,51 +27,28 @@
#include <framework/core/clock.h> #include <framework/core/clock.h>
#include <framework/core/eventdispatcher.h> #include <framework/core/eventdispatcher.h>
Effect::Effect() : Thing() void Effect::draw(const Point& dest, float scaleFactor, bool animate)
{ {
m_animationStartTicks = 0; if(m_id == 0)
} return;
void Effect::start()
{
m_animationStartTicks = g_clock.ticks();
auto self = asEffect(); int animationPhase = 0;
if(animate)
// schedule update animationPhase = std::min((int)(m_animationTimer.ticksElapsed() / Otc::EFFECT_TICKS_PER_FRAME), getAnimationPhases() - 1);
if(getAnimationPhases() > 1) { internalDraw(dest, scaleFactor, 0, 0, 0, animationPhase);
g_dispatcher.scheduleEvent([self]() {
self->updateAnimation();
}, TICKS_PER_FRAME);
}
// schedule removal
g_dispatcher.scheduleEvent([self]() {
g_map.removeThing(self);
}, TICKS_PER_FRAME * getAnimationPhases());
} }
void Effect::draw(const Point& p, const Rect&) void Effect::startAnimation()
{ {
internalDraw(p, 0); m_animationTimer.restart();
}
void Effect::updateAnimation()
{
int animationPhase = (g_clock.ticks() - m_animationStartTicks) / TICKS_PER_FRAME;
if(animationPhase < getAnimationPhases()) // schedule removal
m_animation = animationPhase; auto self = asEffect();
g_dispatcher.scheduleEvent([self]() { g_map.removeThing(self); }, Otc::EFFECT_TICKS_PER_FRAME * getAnimationPhases());
if(animationPhase < getAnimationPhases() - 1) {
auto self = asEffect();
g_dispatcher.scheduleEvent([self]() {
self->updateAnimation();
}, TICKS_PER_FRAME);
}
} }
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);
} }

@ -24,28 +24,23 @@
#define EFFECT_H #define EFFECT_H
#include <framework/global.h> #include <framework/global.h>
#include <framework/core/timer.h>
#include "thing.h" #include "thing.h"
class Effect : public Thing class Effect : public Thing
{ {
public: public:
enum { void draw(const Point& dest, float scaleFactor, bool animate);
TICKS_PER_FRAME = 75
};
Effect(); void setId(uint32 id);
void startAnimation();
void draw(const Point& p, const Rect&);
void start();
void updateAnimation();
ThingType *getType();
uint32 getId() { return m_id; }
EffectPtr asEffect() { return std::static_pointer_cast<Effect>(shared_from_this()); } EffectPtr asEffect() { return std::static_pointer_cast<Effect>(shared_from_this()); }
private: private:
ticks_t m_animationStartTicks; Timer m_animationTimer;
uint16 m_id;
}; };
#endif #endif

@ -94,7 +94,7 @@ void Game::processLogout()
m_protocolGame = nullptr; m_protocolGame = nullptr;
} }
g_map.clean(); g_map.save();
} }
void Game::processDeath() 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); 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); StaticTextPtr staticText = StaticTextPtr(new StaticText);
staticText->addMessage(name, type, message); staticText->addMessage(name, type, message);
g_map.addThing(staticText, creaturePos); 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) void Game::processContainerAddItem(int containerId, const ItemPtr& item)
{ {
if(item) if(item)
item->setPos(Position(65535, containerId + 0x40, 0)); item->setPosition(Position(65535, containerId + 0x40, 0));
g_lua.callGlobalField("Game", "onContainerAddItem", containerId, item); 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) void Game::processInventoryChange(int slot, const ItemPtr& item)
{ {
if(item) if(item)
item->setPos(Position(65535, slot, 0)); item->setPosition(Position(65535, slot, 0));
g_lua.callGlobalField("Game","onInventoryChange", slot, item); 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) void Game::processCreatureMove(const CreaturePtr& creature, const Position& oldPos, const Position& newPos)
{ {
// animate walk // animate walk
if(oldPos.isInRange(newPos, 1, 1, 0)) if(oldPos.isInRange(newPos, 1, 1))
creature->walk(oldPos, newPos); creature->walk(oldPos, newPos);
} }
@ -237,16 +237,8 @@ void Game::walk(Otc::Direction direction)
if(!m_localPlayer->canWalk(direction)) if(!m_localPlayer->canWalk(direction))
return; return;
// only do prewalks to walkable tiles
// TODO: restore check for blockable tiles TilePtr toTile = g_map.getTile(m_localPlayer->getPosition().translatedToDirection(direction));
/*
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));
if(toTile && toTile->isWalkable()) if(toTile && toTile->isWalkable())
m_localPlayer->preWalk(direction); m_localPlayer->preWalk(direction);
else else
@ -316,7 +308,7 @@ void Game::look(const ThingPtr& thing)
int stackpos = getThingStackpos(thing); int stackpos = getThingStackpos(thing);
if(stackpos != -1) 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) void Game::open(const ThingPtr& thing, int containerId)
@ -326,7 +318,7 @@ void Game::open(const ThingPtr& thing, int containerId)
int stackpos = getThingStackpos(thing); int stackpos = getThingStackpos(thing);
if(stackpos != -1) 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) void Game::use(const ThingPtr& thing)
@ -338,7 +330,7 @@ void Game::use(const ThingPtr& thing)
int stackpos = getThingStackpos(thing); int stackpos = getThingStackpos(thing);
if(stackpos != -1) 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) 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()) if(!isOnline() || !fromThing || !toThing || !checkBotProtection())
return; return;
Position pos = fromThing->getPos(); Position pos = fromThing->getPosition();
int fromStackpos = getThingStackpos(fromThing); int fromStackpos = getThingStackpos(fromThing);
if(fromStackpos == -1) if(fromStackpos == -1)
return; return;
@ -360,7 +352,7 @@ void Game::useWith(const ThingPtr& fromThing, const ThingPtr& toThing)
if(toStackpos == -1) if(toStackpos == -1)
return; 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()) { if(CreaturePtr creature = toThing->asCreature()) {
m_protocolGame->sendUseOnCreature(pos, itemId, 0, creature->getId()); m_protocolGame->sendUseOnCreature(pos, itemId, 0, creature->getId());
} else { } 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) 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; return;
m_localPlayer->lockWalk(); m_localPlayer->lockWalk();
@ -394,7 +386,7 @@ void Game::move(const ThingPtr& thing, const Position& toPos, int count)
if(stackpos == -1) if(stackpos == -1)
return; 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) void Game::attack(const CreaturePtr& creature)
@ -433,6 +425,9 @@ void Game::follow(const CreaturePtr& creature)
void Game::cancelFollow() void Game::cancelFollow()
{ {
if(!isOnline() || !checkBotProtection())
return;
m_localPlayer->setFollowingCreature(nullptr); m_localPlayer->setFollowingCreature(nullptr);
m_protocolGame->sendFollow(0); m_protocolGame->sendFollow(0);
} }
@ -444,16 +439,15 @@ void Game::rotate(const ThingPtr& thing)
int stackpos = getThingStackpos(thing); int stackpos = getThingStackpos(thing);
if(stackpos != -1) 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 //TODO: move this to Thing class
int Game::getThingStackpos(const ThingPtr& thing) int Game::getThingStackpos(const ThingPtr& thing)
{ {
// thing is at map // thing is at map
if(thing->getPos().x != 65535) { if(thing->getPosition().x != 65535) {
TilePtr tile = g_map.getTile(thing->getPos()); if(TilePtr tile = g_map.getTile(thing->getPosition()))
if(tile)
return tile->getThingStackpos(thing); return tile->getThingStackpos(thing);
else { else {
logError("could not get tile"); logError("could not get tile");
@ -467,21 +461,28 @@ int Game::getThingStackpos(const ThingPtr& thing)
void Game::talk(const std::string& message) 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()) if(!isOnline() || !checkBotProtection())
return; 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()) if(!isOnline() || !checkBotProtection())
return; return;
m_protocolGame->sendTalk(speakTypeDesc, 0, receiver, message); m_protocolGame->sendOpenPrivateChannel(receiver);
} }
void Game::requestChannels() void Game::requestChannels()

@ -52,7 +52,7 @@ public:
double magicLevel, double magicLevelPercent, double magicLevel, double magicLevelPercent,
double soul, double stamina); double soul, double stamina);
void processTextMessage(const std::string& type, const std::string& message); 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 processContainerAddItem(int containerId, const ItemPtr& item);
void processInventoryChange(int slot, const ItemPtr& item); void processInventoryChange(int slot, const ItemPtr& item);
void processCreatureMove(const CreaturePtr& creature, const Position& oldPos, const Position& newPos); void processCreatureMove(const CreaturePtr& creature, const Position& oldPos, const Position& newPos);
@ -82,8 +82,9 @@ public:
// talk related // talk related
void talk(const std::string& message); void talk(const std::string& message);
void talkChannel(const std::string& speakTypeDesc, int channelId, const std::string& message); void talkChannel(Otc::SpeakType speakType, int channelId, const std::string& message);
void talkPrivate(const std::string& speakTypeDesc, const std::string& receiver, 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 requestChannels();
void joinChannel(int channelId); void joinChannel(int channelId);
void leaveChannel(int channelId); void leaveChannel(int channelId);

@ -24,143 +24,181 @@
#include "thingstype.h" #include "thingstype.h"
#include "spritemanager.h" #include "spritemanager.h"
#include "thing.h" #include "thing.h"
#include "tile.h"
#include <framework/core/clock.h> #include <framework/core/clock.h>
#include <framework/core/eventdispatcher.h> #include <framework/core/eventdispatcher.h>
#include <framework/graphics/graphics.h>
#include <framework/graphics/paintershaderprogram.h>
#include <framework/graphics/paintershadersources.h>
Item::Item() : Thing() Item::Item() : Thing()
{ {
m_data = 1; m_id = 0;
m_countOrSubType = 0;
} }
ItemPtr Item::create(int id) 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); ItemPtr item = ItemPtr(new Item);
item->setId(id); item->setId(id);
return item; return item;
} }
void Item::draw(const Point& p, const Rect&) PainterShaderProgramPtr itemProgram;
{
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);
}
void Item::setPos(const Position& position) void Item::draw(const Point& dest, float scaleFactor, bool animate)
{ {
if(m_type->properties[ThingType::IsGround]) { if(m_id == 0)
m_xPattern = position.x % m_type->dimensions[ThingType::PatternX]; return;
m_yPattern = position.y % m_type->dimensions[ThingType::PatternY];
m_zPattern = position.z % m_type->dimensions[ThingType::PatternZ];
}
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) // determine x,y,z patterns
{ int xPattern = 0, yPattern = 0, zPattern = 0;
if(m_type->properties[ThingType::IsStackable] && m_type->dimensions[ThingType::PatternX] == 4 && m_type->dimensions[ThingType::PatternY] == 2) { if(isGround()) {
if(data < 5) { xPattern = m_position.x % getNumPatternsX();
m_xPattern = data-1; yPattern = m_position.y % getNumPatternsY();
m_yPattern = 0; zPattern = m_position.z % getNumPatternsZ();
} } else if(isStackable() && getNumPatternsX() == 4 && getNumPatternsY() == 2) {
else if(data < 10) { if(m_countOrSubType < 5) {
m_xPattern = 0; xPattern = m_countOrSubType-1;
m_yPattern = 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) { } else if(isHangable()) {
m_xPattern = 1; const TilePtr& tile = getTile();
m_yPattern = 1; if(tile) {
if(tile->mustHookSouth())
xPattern = getNumPatternsX() >= 2 ? 1 : 0;
else if(tile->mustHookSouth())
xPattern = getNumPatternsX() >= 3 ? 2 : 0;
} }
else if(data < 50) { } else if(isFluid() || isFluidContainer()) {
m_xPattern = 2; int color = Otc::FluidTransparent;
m_yPattern = 1; switch(m_countOrSubType) {
} case Otc::FluidNone:
else if(data <= 100) { color = Otc::FluidTransparent;
m_xPattern = 3; break;
m_yPattern = 1; 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]) { // setup item drawing shader
m_xPattern = m_type->dimensions[ThingType::PatternX] >= 2 ? 1 : 0; if(!itemProgram) {
} itemProgram = PainterShaderProgramPtr(new PainterShaderProgram);
else if(m_type->properties[ThingType::HookEast]) { itemProgram->addShaderFromSourceCode(Shader::Vertex, glslMainWithTexCoordsVertexShader + glslPositionOnlyVertexShader);
m_xPattern = m_type->dimensions[ThingType::PatternX] >= 3 ? 2 : 0; itemProgram->addShaderFromSourceFile(Shader::Fragment, "/game_shaders/item.frag");
} assert(itemProgram->link());
} }
else if(m_type->properties[ThingType::IsFluid] || m_type->properties[ThingType::IsFluidContainer]) { g_painter.setCustomProgram(itemProgram);
int color = Otc::FluidTransparent;
switch(data) { // now we can draw the item
case Otc::FluidNone: internalDraw(dest, scaleFactor, xPattern, yPattern, zPattern, animationPhase);
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;
}
m_xPattern = (color % 4) % m_type->dimensions[ThingType::PatternX]; // release draw shader
m_yPattern = (color / 4) % m_type->dimensions[ThingType::PatternY]; 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;
} }

@ -33,22 +33,23 @@ public:
static ItemPtr create(int id); static ItemPtr create(int id);
enum { void draw(const Point& dest, float scaleFactor, bool animate);
TICKS_PER_FRAME = 500
};
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); uint8 getCountOrSubType() { return m_countOrSubType; }
void setData(int data); int getSubType();
int getCount();
int getData() { return m_data; } uint32 getId() { return m_id; }
ThingType *getType();
ItemPtr asItem() { return std::static_pointer_cast<Item>(shared_from_this()); } ItemPtr asItem() { return std::static_pointer_cast<Item>(shared_from_this()); }
private: private:
int m_data; uint16 m_id;
uint8 m_countOrSubType;
}; };
#endif #endif

@ -70,11 +70,11 @@ void LocalPlayer::walk(const Position& oldPos, const Position& newPos)
void LocalPlayer::preWalk(Otc::Direction direction) void LocalPlayer::preWalk(Otc::Direction direction)
{ {
// start walking to direction // start walking to direction
Position newPos = m_pos + Position::getPosFromDirection(direction); Position newPos = m_position.translatedToDirection(direction);
m_preWalking = true; m_preWalking = true;
m_lastPrewalkDone = false; m_lastPrewalkDone = false;
m_lastPrewalkDestionation = newPos; m_lastPrewalkDestionation = newPos;
Creature::walk(m_pos, newPos); Creature::walk(m_position, newPos);
} }
bool LocalPlayer::canWalk(Otc::Direction direction) bool LocalPlayer::canWalk(Otc::Direction direction)
@ -145,6 +145,7 @@ void LocalPlayer::updateWalk()
// update walk animation and offsets // update walk animation and offsets
updateWalkAnimation(totalPixelsWalked); updateWalkAnimation(totalPixelsWalked);
updateWalkOffset(totalPixelsWalked); updateWalkOffset(totalPixelsWalked);
updateWalkingTile();
// terminate walk only when client and server side walk are complated // terminate walk only when client and server side walk are complated
if(m_walking && !m_preWalking && m_walkTimer.ticksElapsed() >= m_walkInterval) if(m_walking && !m_preWalking && m_walkTimer.ticksElapsed() >= m_walkInterval)

@ -28,254 +28,122 @@
#include "missile.h" #include "missile.h"
#include "statictext.h" #include "statictext.h"
#include <framework/graphics/graphics.h> #include <framework/core/eventdispatcher.h>
#include <framework/graphics/framebuffer.h> #include "mapview.h"
#include <framework/graphics/paintershaderprogram.h> #include <framework/core/resourcemanager.h>
#include <framework/graphics/paintershadersources.h>
#include <framework/graphics/texture.h>
Map g_map; 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) { auto it = std::find(m_mapViews.begin(), m_mapViews.end(), mapView);
Size fboSize(m_visibleSize.width() * NUM_TILE_PIXELS, m_visibleSize.height() * NUM_TILE_PIXELS); if(it != m_mapViews.end())
m_framebuffer = FrameBufferPtr(new FrameBuffer(fboSize)); m_mapViews.erase(it);
m_framebuffer->setClearColor(Fw::black); }
m_shaderProgram = PainterShaderProgramPtr(new PainterShaderProgram); void Map::notificateTileUpdateToMapViews(const Position& pos)
m_shaderProgram->addShaderFromSourceCode(Shader::Vertex, glslMainWithTexCoordsVertexShader + glslPositionOnlyVertexShader); {
m_shaderProgram->addShaderFromSourceFile(Shader::Fragment, "/game_shaders/map.frag"); for(const MapViewPtr& mapView : m_mapViews)
assert(m_shaderProgram->link()); mapView->onTileUpdate(pos);
} }
g_painter.setColor(Fw::white); void Map::load()
m_framebuffer->bind(); {
if(!g_resources.fileExists("/map.otcmap"))
return;
// draw offsets std::stringstream in;
LocalPlayerPtr localPlayer = g_game.getLocalPlayer(); g_resources.loadFile("/map.otcmap", in);
if(localPlayer)
m_drawOffset = localPlayer->getWalkOffset(); while(!in.eof()) {
Position pos;
//TODO: cache first/last visible floor in.read((char*)&pos, sizeof(pos));
// draw from bottom floors to top floors
int firstFloor = getFirstVisibleFloor(); uint16 id;
const int lastFloor = MAX_Z-1; in.read((char*)&id, sizeof(id));
for(int iz = lastFloor; iz >= firstFloor; --iz) { while(id != 0xFFFF) {
// draw tiles like linus pauling's rule order ItemPtr item = Item::create(id);
const int numDiagonals = m_size.width() + m_size.height() - 1; if(item->isStackable() || item->isFluidContainer() || item->isFluid()) {
for(int diagonal = 0; diagonal < numDiagonals; ++diagonal) { uint8 countOrSubType;
// loop through / diagonal tiles in.read((char*)&countOrSubType, sizeof(countOrSubType));
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) { item->setCountOrSubType(countOrSubType);
// 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);
}
} }
} addThing(item, pos, 255);
in.read((char*)&id, sizeof(id));
// after drawing all tiles, draw shots
for(const MissilePtr& shot : m_missilesAtFloor[iz]) {
Position missilePos = shot->getPos();
shot->draw(positionTo2D(missilePos) - m_drawOffset, rect);
} }
} }
}
m_framebuffer->release(); void Map::save()
{
std::stringstream out;
g_painter.setCustomProgram(m_shaderProgram);
g_painter.setColor(Fw::white); for(auto& pair : m_tiles) {
m_framebuffer->draw(rect); Position pos = pair.first;
g_painter.releaseCustomProgram(); TilePtr tile = pair.second;
if(!tile || tile->isEmpty())
// calculate stretch factor continue;
float horizontalStretchFactor = rect.width() / (float)(m_visibleSize.width() * NUM_TILE_PIXELS); out.write((char*)&pos, sizeof(pos));
float verticalStretchFactor = rect.height() / (float)(m_visibleSize.height() * NUM_TILE_PIXELS); uint16 id;
for(const ThingPtr& thing : tile->getThings()) {
// draw player names and health bars if(ItemPtr item = thing->asItem()) {
//TODO: this must be cached with creature walks id = item->getId();
for(int x = 0; x < m_visibleSize.width(); ++x) { out.write((char*)&id, sizeof(id));
for(int y = 0; y < m_visibleSize.height(); ++y) { if(item->isStackable() || item->isFluidContainer() || item->isFluid()) {
Position tilePos = Position(m_centralPosition.x + (x - m_centralOffset.x + 1), m_centralPosition.y + (y - m_centralOffset.y + 1), m_centralPosition.z); uint8 countOrSubType = item->getCountOrSubType();
if(const TilePtr& tile = m_tiles[tilePos]) { out.write((char*)&countOrSubType, sizeof(countOrSubType));
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);
} }
} }
} }
id = 0xFFFF;
out.write((char*)&id, sizeof(id));
} }
// draw static text g_resources.saveFile("/map.otcmap", out);
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);
}
} }
void Map::clean() void Map::clean()
{ {
m_tiles.clear(); m_tiles.clear();
m_creatures.clear(); m_knownCreatures.clear();
for(int i=0;i<MAX_Z-1;++i) for(int i=0;i<=Otc::MAX_Z;++i)
m_missilesAtFloor[i].clear(); m_floorMissiles[i].clear();
m_animatedTexts.clear(); m_animatedTexts.clear();
m_staticTexts.clear(); m_staticTexts.clear();
} }
int Map::getFirstVisibleFloor()
{
int firstFloor = 0;
for(int ix = -1; ix <= 1 && firstFloor < m_centralPosition.z; ++ix) {
for(int iy = -1; iy <= 1 && firstFloor < m_centralPosition.z; ++iy) {
Position currentPos(m_centralPosition.x + ix, m_centralPosition.y + iy, m_centralPosition.z);
if((ix == 0 && iy == 0) || isLookPossible(currentPos)) {
Position upperPos = currentPos;
Position perspectivePos = currentPos;
perspectivePos.perspectiveUp();
upperPos.up();
while(upperPos.z >= 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) void Map::addThing(const ThingPtr& thing, const Position& pos, int stackPos)
{ {
if(!thing) if(!thing)
return; return;
Position oldPos = thing->getPos(); TilePtr tile = getOrCreateTile(pos);
bool teleport = false;
if(oldPos.isValid() && !oldPos.isInRange(pos,1,1,0))
teleport = true;
TilePtr tile = getTile(pos);
if(CreaturePtr creature = thing->asCreature()) { if(CreaturePtr creature = thing->asCreature()) {
Position oldPos = thing->getPosition();
tile->addThing(thing, stackPos); tile->addThing(thing, stackPos);
m_creatures[creature->getId()] = creature;
if(teleport) if(oldPos.isValid() && !oldPos.isInRange(pos,1,1))
g_game.processCreatureTeleport(creature); g_game.processCreatureTeleport(creature);
} } else if(MissilePtr missile = thing->asMissile()) {
else if(MissilePtr shot = thing->asMissile()) { m_floorMissiles[pos.z].push_back(missile);
m_missilesAtFloor[shot->getPos().z].push_back(shot); } else if(AnimatedTextPtr animatedText = thing->asAnimatedText()) {
}
else if(AnimatedTextPtr animatedText = thing->asAnimatedText()) {
m_animatedTexts.push_back(animatedText); m_animatedTexts.push_back(animatedText);
} } else if(StaticTextPtr staticText = thing->asStaticText()) {
else if(StaticTextPtr staticText = thing->asStaticText()) {
bool mustAdd = true; bool mustAdd = true;
for(auto it = m_staticTexts.begin(), end = m_staticTexts.end(); it != end; ++it) { for(auto it = m_staticTexts.begin(), end = m_staticTexts.end(); it != end; ++it) {
StaticTextPtr cStaticText = *it; StaticTextPtr cStaticText = *it;
if(cStaticText->getPos() == pos) { if(cStaticText->getPosition() == pos) {
// try to combine messages // try to combine messages
if(cStaticText->addMessage(staticText->getName(), staticText->getMessageType(), staticText->getFirstMessage())) { if(cStaticText->addMessage(staticText->getName(), staticText->getMessageType(), staticText->getFirstMessage())) {
mustAdd = false; mustAdd = false;
break; break;
} } else {
else {
// must add another message and rearrenge current // must add another message and rearrenge current
} }
} }
@ -284,113 +152,249 @@ void Map::addThing(const ThingPtr& thing, const Position& pos, int stackPos)
if(mustAdd) if(mustAdd)
m_staticTexts.push_back(staticText); m_staticTexts.push_back(staticText);
} } else {
else {
tile->addThing(thing, stackPos); tile->addThing(thing, stackPos);
} }
thing->start(); thing->startAnimation();
thing->setPos(pos); thing->setPosition(pos);
notificateTileUpdateToMapViews(pos);
} }
ThingPtr Map::getThing(const Position& pos, int stackPos) 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 tile->getThing(stackPos);
return nullptr; return nullptr;
} }
void Map::removeThingByPos(const Position& pos, int stackPos) bool Map::removeThing(const ThingPtr& thing)
{
if(TilePtr& tile = m_tiles[pos])
tile->removeThingByStackpos(stackPos);
}
void Map::removeThing(const ThingPtr& thing)
{ {
if(!thing) if(!thing)
return; return false;
if(MissilePtr shot = thing->asMissile()) { if(MissilePtr missile = thing->asMissile()) {
auto it = std::find(m_missilesAtFloor[shot->getPos().z].begin(), m_missilesAtFloor[shot->getPos().z].end(), shot); auto it = std::find(m_floorMissiles[missile->getPosition().z].begin(), m_floorMissiles[missile->getPosition().z].end(), missile);
if(it != m_missilesAtFloor[shot->getPos().z].end()) { if(it != m_floorMissiles[missile->getPosition().z].end()) {
m_missilesAtFloor[shot->getPos().z].erase(it); 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); 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); m_animatedTexts.erase(it);
return; return true;
} }
else if(StaticTextPtr staticText = thing->asStaticText()) { } else if(StaticTextPtr staticText = thing->asStaticText()) {
auto it = std::find(m_staticTexts.begin(), m_staticTexts.end(), staticText); 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); 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()]) return false;
tile->removeThing(thing);
} }
TilePtr Map::getTile(const Position& pos) bool Map::removeThingByPos(const Position& pos, int stackPos)
{ {
if(!pos.isValid()) if(TilePtr tile = getTile(pos))
return nullptr; return removeThing(tile->getThing(stackPos));
return false;
}
TilePtr& tile = m_tiles[pos]; TilePtr Map::createTile(const Position& pos)
if(!tile) {
tile = TilePtr(new Tile(pos)); TilePtr tile = TilePtr(new Tile(pos));
m_tiles[pos] = tile;
return 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) void Map::cleanTile(const Position& pos)
{ {
if(TilePtr& tile = m_tiles[pos]) if(TilePtr tile = getTile(pos)) {
tile->clean(); tile->clean();
if(tile->canErase())
m_tiles.erase(m_tiles.find(pos));
notificateTileUpdateToMapViews(pos);
}
} }
void Map::addCreature(const CreaturePtr& creature) void Map::addCreature(const CreaturePtr& creature)
{ {
m_creatures[creature->getId()] = creature; m_knownCreatures[creature->getId()] = creature;
} }
CreaturePtr Map::getCreatureById(uint32 id) CreaturePtr Map::getCreatureById(uint32 id)
{ {
if(g_game.getLocalPlayer() && (uint32)g_game.getLocalPlayer()->getId() == id) LocalPlayerPtr localPlayer = g_game.getLocalPlayer();
return g_game.getLocalPlayer(); if(localPlayer && localPlayer->getId() == id)
return m_creatures[id]; return localPlayer;
return m_knownCreatures[id];
} }
void Map::removeCreatureById(uint32 id) void Map::removeCreatureById(uint32 id)
{ {
m_creatures.erase(id); if(id == 0)
return;
m_knownCreatures.erase(id);
} }
void Map::setCentralPosition(const Position& centralPosition) void Map::setCentralPosition(const Position& centralPosition)
{ {
bool teleported = !m_centralPosition.isInRange(centralPosition, 1,1);
m_centralPosition = centralPosition; 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<CreaturePtr> 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<CreaturePtr> Map::getSpectatorsInRange(const Position& centerPos, bool multiFloor, int xRange, int yRange)
{
return getSpectatorsInRangeEx(centerPos, multiFloor, xRange, xRange, yRange, yRange);
}
std::vector<CreaturePtr> Map::getSpectatorsInRangeEx(const Position& centerPos, bool multiFloor, int minXRange, int maxXRange, int minYRange, int maxYRange)
{
int minZRange = 0;
int maxZRange = 0;
std::vector<CreaturePtr> 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) bool Map::isCovered(const Position& pos, int firstFloor)
m_visibleSize = Size(MAP_VISIBLE_WIDTH, MAP_VISIBLE_HEIGHT); {
// 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)); bool Map::isCompletelyCovered(const Position& pos, int firstFloor)
m_size = m_visibleSize + Size(3, 3); {
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) { bool Map::isAwareOfPosition(const Position& pos)
m_framebuffer->resize(Size(m_visibleSize.width() * NUM_TILE_PIXELS, m_visibleSize.height() * NUM_TILE_PIXELS)); {
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, if(m_centralPosition.z > Otc::SEA_FLOOR)
(m_centralOffset.y - 1 + (position.y - m_centralPosition.y) - (m_centralPosition.z - position.z)) * NUM_TILE_PIXELS); return std::min(m_centralPosition.z+Otc::AWARE_UNDEGROUND_FLOOR_RANGE, (int)Otc::MAX_Z);
else
return Otc::SEA_FLOOR;
} }

@ -26,70 +26,65 @@
#include "creature.h" #include "creature.h"
#include "animatedtext.h" #include "animatedtext.h"
#include <framework/core/clock.h> #include <framework/core/clock.h>
#include <framework/graphics/declarations.h>
class Map class Map
{ {
public: public:
enum { void addMapView(const MapViewPtr& mapView);
MAP_VISIBLE_WIDTH = 15, void removeMapView(const MapViewPtr& mapView);
MAP_VISIBLE_HEIGHT = 11, void notificateTileUpdateToMapViews(const Position& pos);
MAP_SIZE_Z = 8,
MAX_WIDTH = 24,
MAX_HEIGHT = 24,
MAX_Z = 16,
NUM_TILE_PIXELS = 32
};
Map(); void load();
void save();
void draw(const Rect& rect);
void clean(); void clean();
int getFirstVisibleFloor(); // thing related
bool isLookPossible(const Position& pos);
bool isCovered(const Position& pos, int firstFloor = 0);
bool isCompletlyCovered(const Position& pos, int firstFloor = 0);
void addThing(const ThingPtr& thing, const Position& pos, int stackPos = -1); void addThing(const ThingPtr& thing, const Position& pos, int stackPos = -1);
ThingPtr getThing(const Position& pos, int stackPos); ThingPtr getThing(const Position& pos, int stackPos);
void removeThingByPos(const Position& pos, int stackPos); bool removeThingByPos(const Position& pos, int stackPos);
void removeThing(const ThingPtr& thing);
void cleanTile(const Position& pos);
TilePtr getTile(const Position& pos);
void setLight(const Light& light) { m_light = light; } // tile related
Light getLight() { return m_light; } TilePtr createTile(const Position& pos);
const TilePtr& getTile(const Position& pos);
void setCentralPosition(const Position& centralPosition); TilePtr getOrCreateTile(const Position& pos);
Position getCentralPosition() { return m_centralPosition; } void cleanTile(const Position& pos);
bool removeThing(const ThingPtr& thing);
// known creature related
void addCreature(const CreaturePtr& creature); void addCreature(const CreaturePtr& creature);
CreaturePtr getCreatureById(uint32 id); CreaturePtr getCreatureById(uint32 id);
void removeCreatureById(uint32 id); void removeCreatureById(uint32 id);
std::vector<CreaturePtr> getSpectators(const Position& centerPos, bool multiFloor);
std::vector<CreaturePtr> getSpectatorsInRange(const Position& centerPos, bool multiFloor, int xRange, int yRange);
std::vector<CreaturePtr> 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); Light getLight() { return m_light; }
Size getVibibleSize() { return m_visibleSize; } Position getCentralPosition() { return m_centralPosition; }
Point getCentralOffset() { return m_centralOffset; } int getFirstAwareFloor();
int getLastAwareFloor();
const std::vector<MissilePtr>& getFloorMissiles(int z) { return m_floorMissiles[z]; }
Point positionTo2D(const Position& position); std::vector<AnimatedTextPtr> getAnimatedTexts() { return m_animatedTexts; }
std::vector<StaticTextPtr> getStaticTexts() { return m_staticTexts; }
private: private:
std::unordered_map<Position, TilePtr, PositionHasher> m_tiles; std::unordered_map<Position, TilePtr, PositionHasher> m_tiles;
std::map<uint32, CreaturePtr> m_creatures; std::map<uint32, CreaturePtr> m_knownCreatures;
std::array<std::vector<MissilePtr>, MAX_Z> m_missilesAtFloor; std::array<std::vector<MissilePtr>, Otc::MAX_Z+1> m_floorMissiles;
std::vector<AnimatedTextPtr> m_animatedTexts; std::vector<AnimatedTextPtr> m_animatedTexts;
std::vector<StaticTextPtr> m_staticTexts; std::vector<StaticTextPtr> m_staticTexts;
std::vector<MapViewPtr> m_mapViews;
Light m_light; Light m_light;
Position m_centralPosition; 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; extern Map g_map;

@ -0,0 +1,530 @@
/*
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "mapview.h"
#include <framework/graphics/graphics.h>
#include <framework/graphics/framebuffer.h>
#include <framework/graphics/paintershaderprogram.h>
#include <framework/graphics/paintershadersources.h>
#include "creature.h"
#include "map.h"
#include "tile.h"
#include "statictext.h"
#include "animatedtext.h"
#include "missile.h"
#include <framework/core/eventdispatcher.h>
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<Point> 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<fillWidth;++qx) {
//TODO: remvoe this repeated code
// 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) {
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<fillHeight;++qy) {
// 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) {
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<fillWidth;++qx) {
// 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) {
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<fillHeight;++qy) {
// 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) {
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);
}

@ -0,0 +1,118 @@
/*
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef MAPVIEW_H
#define MAPVIEW_H
#include "declarations.h"
#include <framework/graphics/declarations.h>
#include <framework/luascript/luaobject.h>
#include <framework/core/declarations.h>
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<MapView>(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<true> m_mustUpdateVisibleTilesCache;
Boolean<true> m_mustDrawVisibleTilesCache;
Boolean<true> m_mustCleanFramebuffer;
Boolean<true> m_animated;
std::vector<TilePtr> m_cachedVisibleTiles;
std::vector<CreaturePtr> m_cachedFloorVisibleCreatures;
EventPtr m_updateTilesCacheEvent;
CreaturePtr m_followingCreature;
FrameBufferPtr m_framebuffer;
PainterShaderProgramPtr m_shaderProgram;
ViewRange m_viewRange;
};
#endif

@ -27,73 +27,62 @@
#include <framework/core/clock.h> #include <framework/core/clock.h>
#include <framework/core/eventdispatcher.h> #include <framework/core/eventdispatcher.h>
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&) int xPattern = 0, yPattern = 0;
{ if(m_direction == Otc::NorthWest) {
float time = (g_clock.ticks() - m_startTicks) / m_duration; xPattern = 0;
internalDraw(p + Point(m_posDelta.x * time, m_posDelta.y * time), 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) void Missile::setPath(const Position& fromPosition, const Position& toPosition)
{ {
Otc::Direction direction = fromPosition.getDirectionFromPosition(toPosition); m_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_pos = fromPosition; m_position = fromPosition;
m_posDelta = toPosition - fromPosition; m_delta = Point(toPosition.x - fromPosition.x, toPosition.y - fromPosition.y);
m_startTicks = g_clock.ticks(); m_duration = 150 * std::sqrt(m_delta.length());
m_duration = 150 * std::sqrt(Point(m_posDelta.x, m_posDelta.y).length()); m_delta *= Otc::TILE_PIXELS;
m_posDelta.x *= Map::NUM_TILE_PIXELS; m_animationTimer.restart();
m_posDelta.y *= Map::NUM_TILE_PIXELS;
// schedule removal // schedule removal
auto self = asMissile(); auto self = asMissile();
g_dispatcher.scheduleEvent([self]() { g_dispatcher.scheduleEvent([self]() { g_map.removeThing(self); }, m_duration);
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);
} }

@ -24,6 +24,7 @@
#define SHOT_H #define SHOT_H
#include <framework/global.h> #include <framework/global.h>
#include <framework/core/timer.h>
#include "thing.h" #include "thing.h"
class Missile : public Thing class Missile : public Thing
@ -33,22 +34,23 @@ class Missile : public Thing
}; };
public: public:
Missile(); void draw(const Point& dest, float scaleFactor, bool animate);
void draw(const Point& p, const Rect&);
void updateAnimation(); void updateAnimation();
void setId(uint32 id);
void setPath(const Position& fromPosition, const Position& toPosition); void setPath(const Position& fromPosition, const Position& toPosition);
ThingType *getType(); uint32 getId() { return m_id; }
MissilePtr asMissile() { return std::static_pointer_cast<Missile>(shared_from_this()); } MissilePtr asMissile() { return std::static_pointer_cast<Missile>(shared_from_this()); }
private: private:
ticks_t m_startTicks; Timer m_animationTimer;
Position m_posDelta; Point m_delta;
float m_duration; float m_duration;
uint16 m_id;
Otc::Direction m_direction;
}; };
#endif #endif

@ -22,6 +22,7 @@
#include "spritemanager.h" #include "spritemanager.h"
#include <framework/core/resourcemanager.h> #include <framework/core/resourcemanager.h>
#include <framework/core/eventdispatcher.h>
#include <framework/graphics/graphics.h> #include <framework/graphics/graphics.h>
SpriteManager g_sprites; SpriteManager g_sprites;
@ -54,6 +55,21 @@ void SpriteManager::unload()
m_signature = 0; 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<start+burst && i<=m_spritesCount;++i) {
loadSpriteTexture(i);
}
};
for(int i=1;i<=m_spritesCount;i+=burst)
g_dispatcher.scheduleEvent(std::bind(preload, i), (i/burst) * interval);
}
TexturePtr SpriteManager::loadSpriteTexture(int id) TexturePtr SpriteManager::loadSpriteTexture(int id)
{ {
m_fin.seekg(((id-1) * 4) + 6, std::ios_base::beg); m_fin.seekg(((id-1) * 4) + 6, std::ios_base::beg);
@ -74,7 +90,7 @@ TexturePtr SpriteManager::loadSpriteTexture(int id)
uint16 pixelDataSize = Fw::getU16(m_fin); uint16 pixelDataSize = Fw::getU16(m_fin);
uchar pixels[4096]; static std::vector<uint8> pixels(4096);
int writePos = 0; int writePos = 0;
int read = 0; int read = 0;
@ -116,7 +132,10 @@ TexturePtr SpriteManager::loadSpriteTexture(int id)
writePos += 4; 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) TexturePtr SpriteManager::getSpriteTexture(int id)

@ -33,6 +33,7 @@ public:
bool load(const std::string& file); bool load(const std::string& file);
void unload(); void unload();
void preloadSprites();
uint32 getSignature() { return m_signature; } uint32 getSignature() { return m_signature; }
int getSpritesCount() { return m_spritesCount; } int getSpritesCount() { return m_spritesCount; }
@ -45,7 +46,7 @@ private:
Boolean<false> m_loaded; Boolean<false> m_loaded;
uint32 m_signature; uint32 m_signature;
uint16 m_spritesCount; int m_spritesCount;
std::stringstream m_fin; std::stringstream m_fin;
std::vector<TexturePtr> m_sprites; std::vector<TexturePtr> m_sprites;
TexturePtr m_transparentSprite; TexturePtr m_transparentSprite;

@ -31,17 +31,22 @@ StaticText::StaticText()
m_font = g_fonts.getFont("verdana-11px-rounded"); 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; Rect boundRect = rect;
boundRect.bind(visibleRect); boundRect.bind(parentRect);
if((boundRect.center() - rect.center()).length() < visibleRect.width() / 15)
// 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); 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 // First message
if(m_messages.size() == 0) { if(m_messages.size() == 0) {
m_name = name; m_name = name;
@ -59,7 +64,7 @@ bool StaticText::addMessage(const std::string& name, const std::string& type, co
auto self = asStaticText(); auto self = asStaticText();
g_dispatcher.scheduleEvent([self]() { g_dispatcher.scheduleEvent([self]() {
self->removeMessage(); self->removeMessage();
}, std::max<int>(DURATION_PER_CHARACTER * message.length(), MIN_DURATION)); }, std::max<int>(Otc::STATIC_DURATION_PER_CHARACTER * message.length(), Otc::MIN_STATIC_TEXT_DURATION));
return true; return true;
} }
@ -72,40 +77,34 @@ void StaticText::removeMessage()
// schedule removal // schedule removal
auto self = asStaticText(); auto self = asStaticText();
g_dispatcher.addEvent([self]() { g_map.removeThing(self); }); g_dispatcher.addEvent([self]() { g_map.removeThing(self); });
} } else
else
compose(); compose();
} }
void StaticText::compose() void StaticText::compose()
{ {
//TODO: this could be moved to lua
std::string text; std::string text;
text.clear();
if(m_messageType == "say") { if(m_messageType == Otc::SpeakSay) {
text += m_name; text += m_name;
text += " says:\n"; text += " says:\n";
m_color = Color(239, 239, 0); m_color = Color(239, 239, 0);
} } else if(m_messageType == Otc::SpeakWhisper) {
else if(m_messageType == "whisper") {
text += m_name; text += m_name;
text += " whispers:\n"; text += " whispers:\n";
m_color = Color(239, 239, 0); m_color = Color(239, 239, 0);
} } else if(m_messageType == Otc::SpeakYell) {
else if(m_messageType == "yell") {
text += m_name; text += m_name;
text += " yells:\n"; text += " yells:\n";
m_color = Color(239, 239, 0); m_color = Color(239, 239, 0);
} } else if(m_messageType == Otc::SpeakMonsterSay || m_messageType == Otc::SpeakMonsterYell) {
else if(m_messageType == "monsterSay" || m_messageType == "monsterYell") {
m_color = Color(254, 101, 0); m_color = Color(254, 101, 0);
} } else if(m_messageType == Otc::SpeakPrivateNpcToPlayer) {
else if(m_messageType == "npcToPlayer") {
text += m_name; text += m_name;
text += " says:\n"; text += " says:\n";
m_color = Color(95, 247, 247); m_color = Color(95, 247, 247);
} } else {
else {
logWarning("unknown speak type: ", m_messageType); logWarning("unknown speak type: ", m_messageType);
} }
@ -116,6 +115,6 @@ void StaticText::compose()
text += "\n"; 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); m_textSize = m_font->calculateTextRectSize(m_text);
} }

@ -29,20 +29,17 @@
class StaticText : public Thing class StaticText : public Thing
{ {
public: public:
enum {
DURATION_PER_CHARACTER = 75,
MIN_DURATION = 3000
};
StaticText(); 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 getName() { return m_name; }
std::string getMessageType() { return m_messageType; } Otc::SpeakType getMessageType() { return m_messageType; }
std::string getFirstMessage() { return m_messages[0]; } 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(); void removeMessage();
StaticTextPtr asStaticText() { return std::static_pointer_cast<StaticText>(shared_from_this()); } StaticTextPtr asStaticText() { return std::static_pointer_cast<StaticText>(shared_from_this()); }
@ -52,9 +49,10 @@ private:
FontPtr m_font; FontPtr m_font;
Size m_textSize; Size m_textSize;
Boolean<false> m_yell;
std::vector<std::string> m_messages; std::vector<std::string> m_messages;
std::string m_name, m_text; std::string m_name, m_text;
std::string m_messageType; Otc::SpeakType m_messageType;
Color m_color; Color m_color;
}; };

@ -24,59 +24,59 @@
#include "spritemanager.h" #include "spritemanager.h"
#include "thingstype.h" #include "thingstype.h"
#include <framework/graphics/graphics.h> #include <framework/graphics/graphics.h>
#include "map.h"
#include "tile.h"
Thing::Thing() Thing::Thing()
{ {
m_id = 0; m_type = g_thingsType.getEmptyThingType();
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();
} }
int Thing::getStackPriority() int Thing::getStackPriority()
{ {
if(m_type->properties[ThingType::IsGround]) if(isGround())
return 0; return 0;
else if(m_type->properties[ThingType::IsGroundBorder]) else if(isGroundBorder())
return 1; return 1;
else if(m_type->properties[ThingType::IsOnBottom]) else if(isOnBottom())
return 2; return 2;
else if(m_type->properties[ThingType::IsOnTop]) else if(isOnTop())
return 3; return 3;
else if(asCreature()) else if(asCreature())
return 4; 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);
}

@ -24,7 +24,7 @@
#define THING_H #define THING_H
#include "declarations.h" #include "declarations.h"
#include "thingtype.h" #include "thingstype.h"
#include <framework/luascript/luaobject.h> #include <framework/luascript/luaobject.h>
struct Light struct Light
@ -39,23 +39,17 @@ public:
Thing(); Thing();
virtual ~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 uint32 getId() { return 0; }
virtual void setPos(const Position& position) { m_pos = position; } Position getPosition() { return m_position; }
uint32 getId() const { return m_id; }
Position getPos() const { return m_pos; }
int getStackPriority(); int getStackPriority();
virtual ThingType *getType(); const TilePtr& getTile();
int getAnimationPhases() { return m_type->dimensions[ThingType::AnimationPhases]; } int getStackpos();
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; }
ThingPtr asThing() { return std::static_pointer_cast<Thing>(shared_from_this()); } ThingPtr asThing() { return std::static_pointer_cast<Thing>(shared_from_this()); }
virtual ItemPtr asItem() { return nullptr; } virtual ItemPtr asItem() { return nullptr; }
@ -69,29 +63,55 @@ public:
virtual AnimatedTextPtr asAnimatedText() { return nullptr; } virtual AnimatedTextPtr asAnimatedText() { return nullptr; }
virtual StaticTextPtr asStaticText() { return nullptr; } virtual StaticTextPtr asStaticText() { return nullptr; }
// type related
bool isGround() { return m_type->properties[ThingType::IsGround]; } 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 isGroundBorder() { return m_type->properties[ThingType::IsGroundBorder]; }
bool isOnBottom() { return m_type->properties[ThingType::IsOnBottom]; } bool isOnBottom() { return m_type->properties[ThingType::IsOnBottom]; }
bool isOnTop() { return m_type->properties[ThingType::IsOnTop]; } 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 isContainer() { return m_type->properties[ThingType::IsContainer]; }
bool isForceUse() { return m_type->properties[ThingType::IsForceUse]; } bool isForceUse() { return m_type->properties[ThingType::IsForceUse]; }
bool isMultiUse() { return m_type->properties[ThingType::IsMultiUse]; } bool isMultiUse() { return m_type->properties[ThingType::IsMultiUse]; }
bool isRotateable() { return m_type->properties[ThingType::IsRotateable]; } bool isRotateable() { return m_type->properties[ThingType::IsRotateable]; }
bool isNotMoveable() { return m_type->properties[ThingType::IsNotMovable]; } 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 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 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 isFluid() { return m_type->properties[ThingType::IsFluid]; }
bool isFluidContainer() { return m_type->properties[ThingType::IsFluidContainer]; } 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: 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_position;
Position m_pos;
ThingType *m_type; ThingType *m_type;
int m_xPattern, m_yPattern, m_zPattern, m_animation;
}; };
#endif #endif

@ -115,11 +115,12 @@ void ThingsType::parseThingType(std::stringstream& fin, ThingType& thingType)
ThingType *ThingsType::getThingType(uint16 id, Categories category) ThingType *ThingsType::getThingType(uint16 id, Categories category)
{ {
assert(id != 0);
if(category == Item) if(category == Item)
id -= 100; id -= 100;
else if(category == Creature || category == Effect || category == Missile) else if(category == Creature || category == Effect || category == Missile)
id -= 1; id -= 1;
assert(id < m_things[category].size());
if(id == 0 || id >= m_things[category].size())
return &m_emptyThingType;
return &m_things[category][id]; return &m_things[category][id];
} }

@ -49,6 +49,9 @@ public:
uint32 getSignature() { return m_signature; } uint32 getSignature() { return m_signature; }
bool isLoaded() { return m_loaded; } bool isLoaded() { return m_loaded; }
uint16 getFirstItemId() { return 100; }
uint16 getMaxItemid() { return m_things[Item].size() + 99; }
private: private:
uint32 m_signature; uint32 m_signature;
Boolean<false> m_loaded; Boolean<false> m_loaded;

@ -33,70 +33,117 @@
Tile::Tile(const Position& position) Tile::Tile(const Position& position)
{ {
m_drawElevation = 0; 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; m_drawElevation = 0;
bool animate = drawFlags & Otc::DrawAnimations;
// first bottom items // first bottom items
for(const ThingPtr& thing : m_things) { if(drawFlags & (Otc::DrawGround | Otc::DrawGroundBorders | Otc::DrawOnBottom)) {
ThingType *type = thing->getType(); for(const ThingPtr& thing : m_things) {
if(!type->properties[ThingType::IsGround] && !type->properties[ThingType::IsGroundBorder] && !type->properties[ThingType::IsOnBottom]) if(!thing->isGround() && !thing->isGroundBorder() && !thing->isOnBottom())
break; break;
thing->draw(p - m_drawElevation, visibleRect);
m_drawElevation += type->parameters[ThingType::Elevation]; if((thing->isGround() && drawFlags & Otc::DrawGround) ||
if(m_drawElevation > MAX_DRAW_ELEVATION) (thing->isGroundBorder() && drawFlags & Otc::DrawGroundBorders) ||
m_drawElevation = MAX_DRAW_ELEVATION; (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 int redrawPreviousTopW = 0;
for(auto it = m_things.rbegin(); it != m_things.rend(); ++it) { int redrawPreviousTopH = 0;
const ThingPtr& thing = *it;
ThingType *type = thing->getType(); if(drawFlags & Otc::DrawItems) {
if(thing->asCreature() || type->properties[ThingType::IsOnTop] || type->properties[ThingType::IsOnBottom] || type->properties[ThingType::IsGroundBorder] || type->properties[ThingType::IsGround]) // now common items in reverse order
break; for(auto it = m_things.rbegin(); it != m_things.rend(); ++it) {
thing->draw(p - m_drawElevation, visibleRect); const ThingPtr& thing = *it;
m_drawElevation += type->parameters[ThingType::Elevation]; if(thing->isOnTop() || thing->isOnBottom() || thing->isGroundBorder() || thing->isGround() || thing->asCreature())
if(m_drawElevation > MAX_DRAW_ELEVATION) break;
m_drawElevation = MAX_DRAW_ELEVATION; 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 // after we render 2x2 lying corpses, we must redraw previous creatures/ontop above them
//TODO: this algorithm is slowing down render too much, but it could be cached to improve framerate if(redrawPreviousTopH > 0 || redrawPreviousTopW > 0) {
//NOTE: looping for 9 tiles is a dirty way to render walking creatures, must change this later int topRedrawFlags = drawFlags & (Otc::DrawCreatures | Otc::DrawEffects | Otc::DrawOnTop | Otc::DrawAnimations);
for(int xi = -1; xi <= 1; ++xi) { if(topRedrawFlags) {
for(int yi = -1; yi <= 1; ++yi) { for(int y=-redrawPreviousTopH;y<=0;++y) {
for(CreaturePtr creature : g_map.getTile(m_pos + Position(xi, yi, 0))->getCreatures()) { for(int x=-redrawPreviousTopW;x<=0;++x) {
ThingType *type = creature->getType(); if(x == 0 && y == 0)
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); continue;
Rect thisTileRect(p.x, p.y, 32, 32); const TilePtr& tile = g_map.getTile(m_position.translated(x,y));
if(tile)
// only render creatures where bottom right is inside our rect tile->draw(dest + Point(x*Otc::TILE_PIXELS, y*Otc::TILE_PIXELS)*scaleFactor, scaleFactor, topRedrawFlags);
if(thisTileRect.contains(creatureRect.bottomRight())) {
creature->draw(Point(p.x + xi*32 - m_drawElevation, p.y + yi*32 - m_drawElevation), visibleRect);
} }
} }
} }
} }
// 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 // effects
for(const EffectPtr& effect : m_effects) if(drawFlags & Otc::DrawEffects) {
effect->draw(p - m_drawElevation, visibleRect); for(const EffectPtr& effect : m_effects)
effect->draw(dest - m_drawElevation*scaleFactor, scaleFactor, animate);
}
// top items // top items
for(const ThingPtr& thing : m_things) { if(drawFlags & Otc::DrawOnTop) {
ThingType *type = thing->getType(); for(const ThingPtr& thing : m_things) {
if(type->properties[ThingType::IsOnTop]) if(thing->isOnTop())
thing->draw(p, visibleRect); thing->draw(dest, scaleFactor, animate);
}
} }
} }
void Tile::clean() void Tile::clean()
{ {
m_things.clear(); while(!m_things.empty())
m_effects.clear(); 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) 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()) if(stackPos < (int)m_things.size())
oldObject = m_things[stackPos]; oldObject = m_things[stackPos];
m_things.insert(m_things.begin() + stackPos, thing); m_things.insert(m_things.begin() + stackPos, thing);
return oldObject; 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) ThingPtr Tile::getThing(int stackPos)
{ {
if(stackPos >= 0 && stackPos < (int)m_things.size()) if(stackPos >= 0 && stackPos < (int)m_things.size())
@ -150,33 +228,6 @@ ThingPtr Tile::getTopThing()
return m_things[m_things.size() - 1]; 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<CreaturePtr> Tile::getCreatures() std::vector<CreaturePtr> Tile::getCreatures()
{ {
std::vector<CreaturePtr> creatures; std::vector<CreaturePtr> creatures;
@ -192,8 +243,7 @@ ItemPtr Tile::getGround()
ThingPtr firstObject = getThing(0); ThingPtr firstObject = getThing(0);
if(!firstObject) if(!firstObject)
return nullptr; return nullptr;
ThingType *type = firstObject->getType(); if(firstObject->isGround())
if(type->properties[ThingType::IsGround])
return firstObject->asItem(); return firstObject->asItem();
return nullptr; return nullptr;
} }
@ -213,7 +263,7 @@ ThingPtr Tile::getTopLookThing()
for(uint i = 0; i < m_things.size(); ++i) { for(uint i = 0; i < m_things.size(); ++i) {
ThingPtr thing = m_things[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; return thing;
} }
@ -239,7 +289,7 @@ CreaturePtr Tile::getTopCreature()
CreaturePtr creature; CreaturePtr creature;
for(uint i = 0; i < m_things.size(); ++i) { for(uint i = 0; i < m_things.size(); ++i) {
ThingPtr thing = m_things[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(); creature = thing->asCreature();
else if(thing->asCreature() && !thing->asLocalPlayer()) else if(thing->asCreature() && !thing->asLocalPlayer())
return thing->asCreature(); return thing->asCreature();
@ -288,8 +338,7 @@ bool Tile::isWalkable()
return false; return false;
for(const ThingPtr& thing : m_things) { for(const ThingPtr& thing : m_things) {
ThingType *type = thing->getType(); if(thing->isNotWalkable())
if(type->properties[ThingType::NotWalkable])
return false; return false;
if(CreaturePtr creature = thing->asCreature()) { if(CreaturePtr creature = thing->asCreature()) {
@ -302,11 +351,8 @@ bool Tile::isWalkable()
bool Tile::isFullGround() bool Tile::isFullGround()
{ {
ThingPtr ground = getThing(0); ItemPtr ground = getGround();
if(!ground) if(ground && ground->isFullGround())
return false;
ThingType *type = ground->getType();
if(type->properties[ThingType::IsGround] && type->properties[ThingType::IsFullGround])
return true; return true;
return false; return false;
} }
@ -314,19 +360,13 @@ bool Tile::isFullGround()
bool Tile::isFullyOpaque() bool Tile::isFullyOpaque()
{ {
ThingPtr firstObject = getThing(0); ThingPtr firstObject = getThing(0);
if(firstObject) { return firstObject && firstObject->isFullGround();
ThingType *type = firstObject->getType();
if(type->properties[ThingType::IsFullGround])
return true;
}
return false;
} }
bool Tile::isLookPossible() bool Tile::isLookPossible()
{ {
for(const ThingPtr& thing : m_things) { for(const ThingPtr& thing : m_things) {
ThingType *type = thing->getType(); if(thing->blocksProjectile())
if(type->properties[ThingType::BlockProjectile])
return false; return false;
} }
return true; return true;
@ -334,14 +374,15 @@ bool Tile::isLookPossible()
bool Tile::isClickable() 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) { for(const ThingPtr& thing : m_things) {
ThingType *type = thing->getType(); if(thing->isGround())
if(type->properties[ThingType::IsGround])
hasGround = true; hasGround = true;
if(type->properties[ThingType::IsOnBottom]) if(thing->isOnBottom())
hasOnBottom = true; hasOnBottom = true;
if(type->properties[ThingType::IgnoreLook]) if(thing->isIgnoreLook())
hasIgnoreLook = true; hasIgnoreLook = true;
if((hasGround || hasOnBottom) && !hasIgnoreLook) if((hasGround || hasOnBottom) && !hasIgnoreLook)
@ -350,6 +391,27 @@ bool Tile::isClickable()
return false; 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() bool Tile::hasCreature()
{ {
for(const ThingPtr& thing : m_things) for(const ThingPtr& thing : m_things)
@ -358,7 +420,17 @@ bool Tile::hasCreature()
return false; 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();
}

@ -28,22 +28,22 @@
class Tile : public LuaObject class Tile : public LuaObject
{ {
enum {
MAX_DRAW_ELEVATION = 24
};
public: public:
Tile(const Position& position); 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 clean();
void addWalkingCreature(const CreaturePtr& creature);
void removeWalkingCreature(const CreaturePtr& creature);
ThingPtr addThing(const ThingPtr& thing, int stackPos = -1); ThingPtr addThing(const ThingPtr& thing, int stackPos = -1);
bool removeThing(ThingPtr thing);
ThingPtr getThing(int stackPos); ThingPtr getThing(int stackPos);
int getThingStackpos(const ThingPtr& thing); int getThingStackpos(const ThingPtr& thing);
ThingPtr getTopThing(); ThingPtr getTopThing();
ThingPtr removeThingByStackpos(int stackPos);
ThingPtr removeThing(const ThingPtr& thing);
ThingPtr getTopLookThing(); ThingPtr getTopLookThing();
ThingPtr getTopUseThing(); ThingPtr getTopUseThing();
@ -51,26 +51,33 @@ public:
ThingPtr getTopMoveThing(); ThingPtr getTopMoveThing();
ThingPtr getTopMultiUseThing(); ThingPtr getTopMultiUseThing();
const Position& getPos() { return m_pos; } const Position& getPosition() { return m_position; }
int getDrawElevation() { return m_drawElevation; } int getDrawElevation() { return m_drawElevation; }
std::vector<CreaturePtr> getCreatures(); std::vector<CreaturePtr> getCreatures();
const std::vector<ThingPtr>& getThings() { return m_things; }
ItemPtr getGround(); ItemPtr getGround();
int getGroundSpeed(); int getGroundSpeed();
int getThingCount() { return m_things.size() + m_effects.size(); }
bool isWalkable(); bool isWalkable();
bool isFullGround(); bool isFullGround();
bool isFullyOpaque(); bool isFullyOpaque();
bool isLookPossible(); bool isLookPossible();
bool hasCreature();
bool isEmpty();
bool isClickable(); bool isClickable();
bool isEmpty();
bool mustHookSouth();
bool mustHookEast();
bool hasCreature();
bool limitsFloorsView();
bool canErase();
TilePtr asTile() { return std::static_pointer_cast<Tile>(shared_from_this()); } TilePtr asTile() { return std::static_pointer_cast<Tile>(shared_from_this()); }
private: private:
std::vector<EffectPtr> m_effects; // Leave this outside m_things because it has no stackpos. std::vector<CreaturePtr> m_walkingCreatures;
std::vector<EffectPtr> m_effects; // leave this outside m_things because it has no stackpos.
std::vector<ThingPtr> m_things; std::vector<ThingPtr> m_things;
Position m_pos; Position m_position;
int m_drawElevation; uint8 m_drawElevation;
}; };
#endif #endif

@ -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", "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", "isLoaded", std::bind(&SpriteManager::isLoaded, &g_sprites));
g_lua.bindClassStaticFunction("g_sprites", "getSignature", std::bind(&SpriteManager::getSignature, &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.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", "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", "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", "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", "getThing", std::bind(&Map::getThing, &g_map, _1, _2));
g_lua.bindClassStaticFunction("g_map", "removeThingByPos", std::bind(&Map::removeThingByPos, &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", "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", "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", "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", "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", "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", "getSpectators", std::bind(&Map::getSpectators, &g_map, _1, _2));
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.bindGlobalFunction("getOufitColor", Outfit::getColor); g_lua.bindGlobalFunction("getOufitColor", Outfit::getColor);
@ -91,14 +87,11 @@ void OTClient::registerLuaFunctions()
g_lua.registerClass<Thing>(); g_lua.registerClass<Thing>();
g_lua.bindClassMemberFunction<Thing>("setId", &Thing::setId); g_lua.bindClassMemberFunction<Thing>("setId", &Thing::setId);
g_lua.bindClassMemberFunction<Thing>("setPos", &Thing::setPos); g_lua.bindClassMemberFunction<Thing>("setPosition", &Thing::setPosition);
g_lua.bindClassMemberFunction<Thing>("getId", &Thing::getId); g_lua.bindClassMemberFunction<Thing>("getId", &Thing::getId);
g_lua.bindClassMemberFunction<Thing>("getPos", &Thing::getPos); g_lua.bindClassMemberFunction<Thing>("getPosition", &Thing::getPosition);
g_lua.bindClassMemberFunction<Thing>("getStackPriority", &Thing::getStackPriority); g_lua.bindClassMemberFunction<Thing>("getStackPriority", &Thing::getStackPriority);
g_lua.bindClassMemberFunction<Thing>("getAnimationPhases", &Thing::getAnimationPhases); g_lua.bindClassMemberFunction<Thing>("getAnimationPhases", &Thing::getAnimationPhases);
g_lua.bindClassMemberFunction<Thing>("setXPattern", &Thing::setXPattern);
g_lua.bindClassMemberFunction<Thing>("setYPattern", &Thing::setYPattern);
g_lua.bindClassMemberFunction<Thing>("setZPattern", &Thing::setZPattern);
g_lua.bindClassMemberFunction<Thing>("asThing", &Thing::asThing); g_lua.bindClassMemberFunction<Thing>("asThing", &Thing::asThing);
g_lua.bindClassMemberFunction<Thing>("asItem", &Thing::asItem); g_lua.bindClassMemberFunction<Thing>("asItem", &Thing::asItem);
g_lua.bindClassMemberFunction<Thing>("asCreature", &Thing::asCreature); g_lua.bindClassMemberFunction<Thing>("asCreature", &Thing::asCreature);
@ -118,8 +111,11 @@ void OTClient::registerLuaFunctions()
g_lua.bindClassMemberFunction<Thing>("isRotateable", &Thing::isRotateable); g_lua.bindClassMemberFunction<Thing>("isRotateable", &Thing::isRotateable);
g_lua.bindClassMemberFunction<Thing>("isNotMoveable", &Thing::isNotMoveable); g_lua.bindClassMemberFunction<Thing>("isNotMoveable", &Thing::isNotMoveable);
g_lua.bindClassMemberFunction<Thing>("isPickupable", &Thing::isPickupable); g_lua.bindClassMemberFunction<Thing>("isPickupable", &Thing::isPickupable);
g_lua.bindClassMemberFunction<Thing>("ignoreLook", &Thing::ignoreLook); g_lua.bindClassMemberFunction<Thing>("isIgnoreLook", &Thing::isIgnoreLook);
g_lua.bindClassMemberFunction<Thing>("isStackable", &Thing::isStackable); g_lua.bindClassMemberFunction<Thing>("isStackable", &Thing::isStackable);
g_lua.bindClassMemberFunction<Thing>("isHookSouth", &Thing::isHookSouth);
g_lua.bindClassMemberFunction<Thing>("isTranslucent", &Thing::isTranslucent);
g_lua.bindClassMemberFunction<Thing>("isFullGround", &Thing::isFullGround);
g_lua.registerClass<Creature, Thing>(); g_lua.registerClass<Creature, Thing>();
g_lua.bindClassMemberFunction<Creature>("getName", &Creature::getName); g_lua.bindClassMemberFunction<Creature>("getName", &Creature::getName);
@ -133,7 +129,7 @@ void OTClient::registerLuaFunctions()
g_lua.registerClass<Item, Thing>(); g_lua.registerClass<Item, Thing>();
g_lua.bindClassStaticFunction<Item>("create", &Item::create); g_lua.bindClassStaticFunction<Item>("create", &Item::create);
g_lua.bindClassMemberFunction<Item>("getData", &Item::getData); g_lua.bindClassMemberFunction<Item>("getCount", &Item::getCount);
g_lua.registerClass<Effect, Thing>(); g_lua.registerClass<Effect, Thing>();
g_lua.registerClass<Missile, Thing>(); g_lua.registerClass<Missile, Thing>();
@ -141,6 +137,7 @@ void OTClient::registerLuaFunctions()
g_lua.registerClass<AnimatedText, Thing>(); g_lua.registerClass<AnimatedText, Thing>();
g_lua.registerClass<Player, Creature>(); g_lua.registerClass<Player, Creature>();
g_lua.bindClassMemberFunction<Creature>("isWalking", &Creature::isWalking);
g_lua.registerClass<Npc, Creature>(); g_lua.registerClass<Npc, Creature>();
g_lua.registerClass<Monster, Creature>(); g_lua.registerClass<Monster, Creature>();
g_lua.registerClass<LocalPlayer, Player>(); g_lua.registerClass<LocalPlayer, Player>();
@ -165,14 +162,13 @@ void OTClient::registerLuaFunctions()
g_lua.bindClassMemberFunction<Tile>("getThing", &Tile::getThing); g_lua.bindClassMemberFunction<Tile>("getThing", &Tile::getThing);
g_lua.bindClassMemberFunction<Tile>("getThingStackpos", &Tile::getThingStackpos); g_lua.bindClassMemberFunction<Tile>("getThingStackpos", &Tile::getThingStackpos);
g_lua.bindClassMemberFunction<Tile>("getTopThing", &Tile::getTopThing); g_lua.bindClassMemberFunction<Tile>("getTopThing", &Tile::getTopThing);
g_lua.bindClassMemberFunction<Tile>("removeThingByStackpos", &Tile::removeThingByStackpos);
g_lua.bindClassMemberFunction<Tile>("removeThing", &Tile::removeThing); g_lua.bindClassMemberFunction<Tile>("removeThing", &Tile::removeThing);
g_lua.bindClassMemberFunction<Tile>("getTopLookThing", &Tile::getTopLookThing); g_lua.bindClassMemberFunction<Tile>("getTopLookThing", &Tile::getTopLookThing);
g_lua.bindClassMemberFunction<Tile>("getTopUseThing", &Tile::getTopUseThing); g_lua.bindClassMemberFunction<Tile>("getTopUseThing", &Tile::getTopUseThing);
g_lua.bindClassMemberFunction<Tile>("getTopCreature", &Tile::getTopCreature); g_lua.bindClassMemberFunction<Tile>("getTopCreature", &Tile::getTopCreature);
g_lua.bindClassMemberFunction<Tile>("getTopMoveThing", &Tile::getTopMoveThing); g_lua.bindClassMemberFunction<Tile>("getTopMoveThing", &Tile::getTopMoveThing);
g_lua.bindClassMemberFunction<Tile>("getTopMultiUseThing", &Tile::getTopMultiUseThing); g_lua.bindClassMemberFunction<Tile>("getTopMultiUseThing", &Tile::getTopMultiUseThing);
g_lua.bindClassMemberFunction<Tile>("getPos", &Tile::getPos); g_lua.bindClassMemberFunction<Tile>("getPosition", &Tile::getPosition);
g_lua.bindClassMemberFunction<Tile>("getDrawElevation", &Tile::getDrawElevation); g_lua.bindClassMemberFunction<Tile>("getDrawElevation", &Tile::getDrawElevation);
g_lua.bindClassMemberFunction<Tile>("getCreatures", &Tile::getCreatures); g_lua.bindClassMemberFunction<Tile>("getCreatures", &Tile::getCreatures);
g_lua.bindClassMemberFunction<Tile>("getGround", &Tile::getGround); g_lua.bindClassMemberFunction<Tile>("getGround", &Tile::getGround);
@ -194,6 +190,9 @@ void OTClient::registerLuaFunctions()
g_lua.bindClassStaticFunction<Game>("requestOutfit", std::bind(&Game::requestOutfit, &g_game)); g_lua.bindClassStaticFunction<Game>("requestOutfit", std::bind(&Game::requestOutfit, &g_game));
g_lua.bindClassStaticFunction<Game>("requestChannels", std::bind(&Game::requestChannels, &g_game)); g_lua.bindClassStaticFunction<Game>("requestChannels", std::bind(&Game::requestChannels, &g_game));
g_lua.bindClassStaticFunction<Game>("joinChannel", std::bind(&Game::joinChannel, &g_game, _1)); g_lua.bindClassStaticFunction<Game>("joinChannel", std::bind(&Game::joinChannel, &g_game, _1));
g_lua.bindClassStaticFunction<Game>("leaveChannel", std::bind(&Game::leaveChannel, &g_game, _1));
g_lua.bindClassStaticFunction<Game>("closeNpcChannel", std::bind(&Game::closeNpcChannel, &g_game));
g_lua.bindClassStaticFunction<Game>("openPrivateChannel", std::bind(&Game::openPrivateChannel, &g_game, _1));
g_lua.bindClassStaticFunction<Game>("setOutfit", std::bind(&Game::setOutfit, &g_game, _1)); g_lua.bindClassStaticFunction<Game>("setOutfit", std::bind(&Game::setOutfit, &g_game, _1));
g_lua.bindClassStaticFunction<Game>("look", std::bind(&Game::look, &g_game, _1)); g_lua.bindClassStaticFunction<Game>("look", std::bind(&Game::look, &g_game, _1));
g_lua.bindClassStaticFunction<Game>("open", std::bind(&Game::open, &g_game, _1, _2)); g_lua.bindClassStaticFunction<Game>("open", std::bind(&Game::open, &g_game, _1, _2));
@ -201,6 +200,7 @@ void OTClient::registerLuaFunctions()
g_lua.bindClassStaticFunction<Game>("useWith", std::bind(&Game::useWith, &g_game, _1, _2)); g_lua.bindClassStaticFunction<Game>("useWith", std::bind(&Game::useWith, &g_game, _1, _2));
g_lua.bindClassStaticFunction<Game>("move", std::bind(&Game::move, &g_game, _1, _2, _3)); g_lua.bindClassStaticFunction<Game>("move", std::bind(&Game::move, &g_game, _1, _2, _3));
g_lua.bindClassStaticFunction<Game>("useInventoryItem", std::bind(&Game::useInventoryItem, &g_game, _1, _2)); g_lua.bindClassStaticFunction<Game>("useInventoryItem", std::bind(&Game::useInventoryItem, &g_game, _1, _2));
g_lua.bindClassStaticFunction<Game>("turn", std::bind(&Game::turn, &g_game, _1));
g_lua.bindClassStaticFunction<Game>("walk", std::bind(&Game::walk, &g_game, _1)); g_lua.bindClassStaticFunction<Game>("walk", std::bind(&Game::walk, &g_game, _1));
g_lua.bindClassStaticFunction<Game>("forceWalk", std::bind(&Game::forceWalk, &g_game, _1)); g_lua.bindClassStaticFunction<Game>("forceWalk", std::bind(&Game::forceWalk, &g_game, _1));
g_lua.bindClassStaticFunction<Game>("attack", std::bind(&Game::attack, &g_game, _1)); g_lua.bindClassStaticFunction<Game>("attack", std::bind(&Game::attack, &g_game, _1));
@ -226,9 +226,16 @@ void OTClient::registerLuaFunctions()
g_lua.bindClassStaticFunction<Game>("getProtocolGame", std::bind(&Game::getProtocolGame, &g_game)); g_lua.bindClassStaticFunction<Game>("getProtocolGame", std::bind(&Game::getProtocolGame, &g_game));
g_lua.registerClass<UIItem, UIWidget>(); g_lua.registerClass<UIItem, UIWidget>();
g_lua.bindClassStaticFunction<UIItem>("create", []{ return UIItemPtr(new UIItem); } ); g_lua.bindClassStaticFunction<UIItem>("create", []{ return UIItemPtr(new UIItem); });
g_lua.bindClassMemberFunction<UIItem>("getItem", &UIItem::getItem); g_lua.bindClassMemberFunction<UIItem>("setItemId", &UIItem::setItemId);
g_lua.bindClassMemberFunction<UIItem>("setItemCount", &UIItem::setItemCount);
g_lua.bindClassMemberFunction<UIItem>("setItem", &UIItem::setItem); g_lua.bindClassMemberFunction<UIItem>("setItem", &UIItem::setItem);
g_lua.bindClassMemberFunction<UIItem>("setVirtual", &UIItem::setVirtual);
g_lua.bindClassMemberFunction<UIItem>("getItemId", &UIItem::getItemId);
g_lua.bindClassMemberFunction<UIItem>("getItemCount", &UIItem::getItemCount);
g_lua.bindClassMemberFunction<UIItem>("getItem", &UIItem::getItem);
g_lua.bindClassMemberFunction<UIItem>("isVirtual", &UIItem::isVirtual);
g_lua.registerClass<UICreature, UIWidget>(); g_lua.registerClass<UICreature, UIWidget>();
g_lua.bindClassStaticFunction<UICreature>("create", []{ return UICreaturePtr(new UICreature); } ); g_lua.bindClassStaticFunction<UICreature>("create", []{ return UICreaturePtr(new UICreature); } );
@ -238,6 +245,8 @@ void OTClient::registerLuaFunctions()
g_lua.registerClass<UIMap, UIWidget>(); g_lua.registerClass<UIMap, UIWidget>();
g_lua.bindClassStaticFunction<UIMap>("create", []{ return UIMapPtr(new UIMap); } ); g_lua.bindClassStaticFunction<UIMap>("create", []{ return UIMapPtr(new UIMap); } );
g_lua.bindClassMemberFunction<UIMap>("getTile", &UIMap::getTile); g_lua.bindClassMemberFunction<UIMap>("getTile", &UIMap::getTile);
g_lua.bindClassMemberFunction<UIMap>("zoomIn", &UIMap::zoomIn);
g_lua.bindClassMemberFunction<UIMap>("zoomOut", &UIMap::zoomOut);
g_lua.registerClass<UIGame, UIWidget>(); g_lua.registerClass<UIGame, UIWidget>();
g_lua.bindClassStaticFunction<UIGame>("create", []{ return UIGamePtr(new UIGame); } ); g_lua.bindClassStaticFunction<UIGame>("create", []{ return UIGamePtr(new UIGame); } );

@ -123,7 +123,7 @@ namespace Proto {
GameServerTalk = 170, GameServerTalk = 170,
GameServerChannels = 171, GameServerChannels = 171,
GameServerOpenChannel = 172, GameServerOpenChannel = 172,
GameServerPrivateChannel = 173, GameServerOpenPrivateChannel = 173,
GameServerRuleViolationChannel = 174, // deprecated in last tibia GameServerRuleViolationChannel = 174, // deprecated in last tibia
GameServerRuleViolationRemove = 175, // deprecated in last tibia GameServerRuleViolationRemove = 175, // deprecated in last tibia
GameServerRuleViolationCancel = 176, // deprecated in last tibia GameServerRuleViolationCancel = 176, // deprecated in last tibia
@ -190,7 +190,7 @@ namespace Proto {
ClientGetChannels = 151, ClientGetChannels = 151,
ClientJoinChannel = 152, ClientJoinChannel = 152,
ClientLeaveChannel = 153, ClientLeaveChannel = 153,
ClientPrivateChannel = 154, ClientOpenPrivateChannel = 154,
ClientCloseNpcChannel = 158, ClientCloseNpcChannel = 158,
ClientSetTactics = 160, ClientSetTactics = 160,
ClientAttack = 161, ClientAttack = 161,
@ -238,29 +238,29 @@ namespace Proto {
SpeakMonsterYell, SpeakMonsterYell,
// removed // removed
SpeakRVRChannel = 255, ServerSpeakRVRChannel = 255,
SpeakRVRAnswer, ServerSpeakRVRAnswer,
SpeakRVRContinue, ServerSpeakRVRContinue,
SpeakChannelRed2 ServerSpeakChannelRed2
#elif PROTOCOL==860 #elif PROTOCOL==860
SpeakSay = 1, ServerSpeakSay = 1,
SpeakWhisper, ServerSpeakWhisper,
SpeakYell, ServerSpeakYell,
SpeakPrivatePlayerToNpc, ServerSpeakPrivatePlayerToNpc,
SpeakPrivateNpcToPlayer, ServerSpeakPrivateNpcToPlayer,
SpeakPrivate, ServerSpeakPrivate,
SpeakChannelYellow, ServerSpeakChannelYellow,
SpeakChannelWhite, ServerSpeakChannelWhite,
SpeakRVRChannel, ServerSpeakRVRChannel,
SpeakRVRAnswer, ServerSpeakRVRAnswer,
SpeakRVRContinue, ServerSpeakRVRContinue,
SpeakBroadcast, ServerSpeakBroadcast,
SpeakChannelRed, ServerSpeakChannelRed,
SpeakPrivateRed, ServerSpeakPrivateRed,
SpeakChannelOrange, ServerSpeakChannelOrange,
SpeakChannelRed2 = 17, ServerSpeakChannelRed2 = 17,
SpeakMonsterSay = 19, ServerSpeakMonsterSay = 19,
SpeakMonsterYell ServerSpeakMonsterYell
#endif #endif
}; };
@ -299,48 +299,48 @@ namespace Proto {
NpcEndId = 0xffffffff NpcEndId = 0xffffffff
}; };
inline std::string translateSpeakType(int type) { inline Otc::SpeakType translateSpeakTypeFromServer(int type) {
switch(type) { switch(type) {
case Proto::SpeakSay: return "say"; case Proto::ServerSpeakSay: return Otc::SpeakSay;
case Proto::SpeakWhisper: return "whisper"; case Proto::ServerSpeakWhisper: return Otc::SpeakWhisper;
case Proto::SpeakYell: return "yell"; case Proto::ServerSpeakYell: return Otc::SpeakYell;
case Proto::SpeakMonsterSay: return "monsterSay"; case Proto::ServerSpeakMonsterSay: return Otc::SpeakMonsterSay;
case Proto::SpeakMonsterYell: return "monsterYell"; case Proto::ServerSpeakMonsterYell: return Otc::SpeakMonsterYell;
case Proto::SpeakPrivateNpcToPlayer: return "npcToPlayer"; case Proto::ServerSpeakPrivateNpcToPlayer: return Otc::SpeakPrivateNpcToPlayer;
case Proto::SpeakChannelYellow: return "channelYellow"; case Proto::ServerSpeakChannelYellow: return Otc::SpeakChannelYellow;
case Proto::SpeakChannelWhite: return "channelWhite"; case Proto::ServerSpeakChannelWhite: return Otc::SpeakChannelWhite;
case Proto::SpeakChannelRed: return "channelRed"; case Proto::ServerSpeakChannelRed: return Otc::SpeakChannelRed;
case Proto::SpeakChannelRed2: return "channelRed"; case Proto::ServerSpeakChannelRed2: return Otc::SpeakChannelRed;
case Proto::SpeakChannelOrange: return "channelOrange"; case Proto::ServerSpeakChannelOrange: return Otc::SpeakChannelOrange;
case Proto::SpeakPrivate: return "private"; case Proto::ServerSpeakPrivate: return Otc::SpeakPrivate;
case Proto::SpeakPrivatePlayerToNpc: return "playerToNpc"; case Proto::ServerSpeakPrivatePlayerToNpc: return Otc::SpeakPrivate;
case Proto::SpeakBroadcast: return "broadcast"; case Proto::ServerSpeakBroadcast: return Otc::SpeakBroadcast;
case Proto::SpeakPrivateRed: return "privateRed"; case Proto::ServerSpeakPrivateRed: return Otc::SpeakPrivateRed;
default: default:
logError("unknown protocol speak type ", type); logError("unknown protocol speak type ", type);
return "unknown"; return Otc::SpeakSay;
} }
} }
inline int translateSpeakTypeDesc(const std::string& type) { inline Proto::ServerSpeakType translateSpeakTypeToServer(int type) {
if(type == "say") return Proto::SpeakSay; switch(type) {
else if(type == "whisper") return Proto::SpeakWhisper; case Otc::SpeakSay: return Proto::ServerSpeakSay;
else if(type == "yell") return Proto::SpeakYell; case Otc::SpeakWhisper: return Proto::ServerSpeakWhisper;
else if(type == "monsterSay") return Proto::SpeakMonsterSay; case Otc::SpeakYell: return Proto::ServerSpeakYell;
else if(type == "monsterYell") return Proto::SpeakMonsterYell; case Otc::SpeakBroadcast: return Proto::ServerSpeakBroadcast;
else if(type == "npcToPlayer") return Proto::SpeakPrivateNpcToPlayer; case Otc::SpeakPrivate: return Proto::ServerSpeakPrivate;
else if(type == "channelYellow") return Proto::SpeakChannelYellow; case Otc::SpeakPrivateRed: return Proto::ServerSpeakPrivateRed;
else if(type == "channelWhite") return Proto::SpeakChannelWhite; case Otc::SpeakPrivatePlayerToNpc: return Proto::ServerSpeakPrivatePlayerToNpc;
else if(type == "channelRed") return Proto::SpeakChannelRed; case Otc::SpeakPrivateNpcToPlayer: return Proto::ServerSpeakPrivateNpcToPlayer;
else if(type == "channelRed") return Proto::SpeakChannelRed2; case Otc::SpeakChannelYellow: return Proto::ServerSpeakChannelYellow;
else if(type == "channelOrange") return Proto::SpeakChannelOrange; case Otc::SpeakChannelWhite: return Proto::ServerSpeakChannelWhite;
else if(type == "private") return Proto::SpeakPrivate; case Otc::SpeakChannelRed: return Proto::ServerSpeakChannelRed;
else if(type == "playerToNpc") return Proto::SpeakPrivatePlayerToNpc; case Otc::SpeakChannelOrange: return Proto::ServerSpeakChannelOrange;
else if(type == "broadcast") return Proto::SpeakBroadcast; case Otc::SpeakMonsterSay: return Proto::ServerSpeakMonsterSay;
else if(type == "privateRed") return Proto::SpeakPrivateRed; case Otc::SpeakMonsterYell: return Proto::ServerSpeakMonsterYell;
else { default:
logError("unknown protocol speak type desc ", type); logError("unknown protocol speak type desc ", type);
return 0; return Proto::ServerSpeakSay;
} }
} }

@ -73,11 +73,11 @@ public:
void sendTextWindow(uint windowTextId, const std::string& text); void sendTextWindow(uint windowTextId, const std::string& text);
void sendHouseWindow(int doorId, uint id, const std::string& text); void sendHouseWindow(int doorId, uint id, const std::string& text);
void sendLookAt(const Position& position, int thingId, int stackpos); 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 sendGetChannels();
void sendJoinChannel(int channelId); void sendJoinChannel(int channelId);
void sendLeaveChannel(int channelId); void sendLeaveChannel(int channelId);
void sendPrivateChannel(const std::string& receiver); void sendOpenPrivateChannel(const std::string& receiver);
void sendCloseNpcChannel(); void sendCloseNpcChannel();
void sendFightTatics(Otc::FightModes fightMode, Otc::ChaseModes chaseMode, bool safeFight); void sendFightTatics(Otc::FightModes fightMode, Otc::ChaseModes chaseMode, bool safeFight);
void sendAttack(uint creatureId); void sendAttack(uint creatureId);
@ -155,9 +155,9 @@ private:
void parseCreatureSpeak(InputMessage& msg); void parseCreatureSpeak(InputMessage& msg);
void parseChannelList(InputMessage& msg); void parseChannelList(InputMessage& msg);
void parseOpenChannel(InputMessage& msg); void parseOpenChannel(InputMessage& msg);
void parseOpenPrivatePlayerChat(InputMessage& msg); void parseOpenPrivateChannel(InputMessage& msg);
void parseCreatePrivateChannel(InputMessage& msg); void parseCreateOwnPrivateChannel(InputMessage& msg);
void parseClosePrivateChannel(InputMessage& msg); void parseCloseChannel(InputMessage& msg);
void parseSafeTradeRequest(InputMessage& msg); void parseSafeTradeRequest(InputMessage& msg);
void parseSafeTradeClose(InputMessage&); void parseSafeTradeClose(InputMessage&);
void parseTextMessage(InputMessage& msg); void parseTextMessage(InputMessage& msg);

@ -195,8 +195,8 @@ void ProtocolGame::parseMessage(InputMessage& msg)
case Proto::GameServerOpenChannel: case Proto::GameServerOpenChannel:
parseOpenChannel(msg); parseOpenChannel(msg);
break; break;
case Proto::GameServerPrivateChannel: case Proto::GameServerOpenPrivateChannel:
parseOpenPrivatePlayerChat(msg); parseOpenPrivateChannel(msg);
break; break;
case Proto::GameServerRuleViolationChannel: case Proto::GameServerRuleViolationChannel:
msg.getU16(); msg.getU16();
@ -210,10 +210,10 @@ void ProtocolGame::parseMessage(InputMessage& msg)
case Proto::GameServerRuleViolationLock: case Proto::GameServerRuleViolationLock:
break; break;
case Proto::GameServerOpenOwnChannel: case Proto::GameServerOpenOwnChannel:
parseCreatePrivateChannel(msg); parseCreateOwnPrivateChannel(msg);
break; break;
case Proto::GameServerCloseChannel: case Proto::GameServerCloseChannel:
parseClosePrivateChannel(msg); parseCloseChannel(msg);
break; break;
case Proto::GameServerMessage: case Proto::GameServerMessage:
parseTextMessage(msg); parseTextMessage(msg);
@ -317,7 +317,7 @@ void ProtocolGame::parseMapDescription(InputMessage& msg)
{ {
Position pos = parsePosition(msg); Position pos = parsePosition(msg);
g_map.setCentralPosition(pos); 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) void ProtocolGame::parseMoveNorth(InputMessage& msg)
@ -325,7 +325,7 @@ void ProtocolGame::parseMoveNorth(InputMessage& msg)
Position pos = g_map.getCentralPosition(); Position pos = g_map.getCentralPosition();
pos.y--; 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); g_map.setCentralPosition(pos);
} }
@ -334,7 +334,7 @@ void ProtocolGame::parseMoveEast(InputMessage& msg)
Position pos = g_map.getCentralPosition(); Position pos = g_map.getCentralPosition();
pos.x++; 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); g_map.setCentralPosition(pos);
} }
@ -343,7 +343,7 @@ void ProtocolGame::parseMoveSouth(InputMessage& msg)
Position pos = g_map.getCentralPosition(); Position pos = g_map.getCentralPosition();
pos.y++; 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); g_map.setCentralPosition(pos);
} }
@ -352,7 +352,7 @@ void ProtocolGame::parseMoveWest(InputMessage& msg)
Position pos = g_map.getCentralPosition(); Position pos = g_map.getCentralPosition();
pos.x--; 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); g_map.setCentralPosition(pos);
} }
@ -407,7 +407,7 @@ void ProtocolGame::parseCreatureMove(InputMessage& msg)
int oldStackpos = msg.getU8(); int oldStackpos = msg.getU8();
Position newPos = parsePosition(msg); Position newPos = parsePosition(msg);
ThingPtr thing = g_map.getTile(oldPos)->getThing(oldStackpos); ThingPtr thing = g_map.getThing(oldPos, oldStackpos);
if(!thing) { if(!thing) {
logTraceError("could not get thing"); logTraceError("could not get thing");
return; return;
@ -566,10 +566,10 @@ void ProtocolGame::parseDistanceMissile(InputMessage& msg)
Position toPos = parsePosition(msg); Position toPos = parsePosition(msg);
int shotId = msg.getU8(); int shotId = msg.getU8();
MissilePtr shot = MissilePtr(new Missile()); MissilePtr missile = MissilePtr(new Missile());
shot->setId(shotId); missile->setId(shotId);
shot->setPath(fromPos, toPos); missile->setPath(fromPos, toPos);
g_map.addThing(shot, fromPos); g_map.addThing(missile, fromPos);
} }
void ProtocolGame::parseCreatureSquare(InputMessage& msg) void ProtocolGame::parseCreatureSquare(InputMessage& msg)
@ -720,43 +720,43 @@ void ProtocolGame::parseCreatureSpeak(InputMessage& msg)
msg.getU32(); // unkSpeak msg.getU32(); // unkSpeak
std::string name = msg.getString(); std::string name = msg.getString();
int level = msg.getU16(); int level = msg.getU16();
int type = msg.getU8(); int serverType = msg.getU8();
int channelId = 0; int channelId = 0;
Position creaturePos; Position creaturePos;
switch(type) { switch(serverType) {
case Proto::SpeakSay: case Proto::ServerSpeakSay:
case Proto::SpeakWhisper: case Proto::ServerSpeakWhisper:
case Proto::SpeakYell: case Proto::ServerSpeakYell:
case Proto::SpeakMonsterSay: case Proto::ServerSpeakMonsterSay:
case Proto::SpeakMonsterYell: case Proto::ServerSpeakMonsterYell:
case Proto::SpeakPrivateNpcToPlayer: case Proto::ServerSpeakPrivateNpcToPlayer:
creaturePos = parsePosition(msg); creaturePos = parsePosition(msg);
break; break;
case Proto::SpeakChannelYellow: case Proto::ServerSpeakChannelYellow:
case Proto::SpeakChannelWhite: case Proto::ServerSpeakChannelWhite:
case Proto::SpeakChannelRed: case Proto::ServerSpeakChannelRed:
case Proto::SpeakChannelRed2: case Proto::ServerSpeakChannelRed2:
case Proto::SpeakChannelOrange: case Proto::ServerSpeakChannelOrange:
channelId = msg.getU16(); channelId = msg.getU16();
break; break;
case Proto::SpeakPrivate: case Proto::ServerSpeakPrivate:
case Proto::SpeakPrivatePlayerToNpc: case Proto::ServerSpeakPrivatePlayerToNpc:
case Proto::SpeakBroadcast: case Proto::ServerSpeakBroadcast:
case Proto::SpeakPrivateRed: case Proto::ServerSpeakPrivateRed:
break; break;
case Proto::SpeakRVRChannel: case Proto::ServerSpeakRVRChannel:
msg.getU32(); msg.getU32();
break; break;
default: default:
logTraceError("unknown speak type ", type); logTraceError("unknown speak type ", serverType);
break; break;
} }
std::string message = msg.getString(); 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) void ProtocolGame::parseChannelList(InputMessage& msg)
@ -780,20 +780,26 @@ void ProtocolGame::parseOpenChannel(InputMessage& msg)
g_lua.callGlobalField("Game", "onOpenChannel", channelId, name); 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 int id = msg.getU16(); // channel id
msg.getString(); // channel name 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) void ProtocolGame::parseTextMessage(InputMessage& msg)
@ -819,11 +825,11 @@ void ProtocolGame::parseFloorChangeUp(InputMessage& msg)
pos.z--; pos.z--;
int skip = 0; int skip = 0;
if(pos.z == 7) if(pos.z == Otc::SEA_FLOOR)
for(int i = 5; i >= 0; i--) for(int i = Otc::SEA_FLOOR - Otc::AWARE_UNDEGROUND_FLOOR_RANGE; i >= 0; i--)
setFloorDescription(msg, pos.x - 8, pos.y - 6, i, 18, 14, 8 - i, &skip); 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 > 7) else if(pos.z > Otc::SEA_FLOOR)
setFloorDescription(msg, pos.x - 8, pos.y - 6, pos.z - 2, 18, 14, 3, &skip); 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.x++;
pos.y++; pos.y++;
@ -836,13 +842,13 @@ void ProtocolGame::parseFloorChangeDown(InputMessage& msg)
pos.z++; pos.z++;
int skip = 0; int skip = 0;
if(pos.z == 8) { if(pos.z == Otc::UNDERGROUND_FLOOR) {
int j, i; int j, i;
for(i = pos.z, j = -1; i < pos.z + 3; ++i, --j) for(i = pos.z, j = -1; i <= pos.z + Otc::AWARE_UNDEGROUND_FLOOR_RANGE; ++i, --j)
setFloorDescription(msg, pos.x - 8, pos.y - 6, i, 18, 14, j, &skip); 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) else if(pos.z > Otc::UNDERGROUND_FLOOR && pos.z < Otc::MAX_Z-1)
setFloorDescription(msg, pos.x - 8, pos.y - 6, pos.z + 2, 18, 14, -3, &skip); 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.x--;
pos.y--; pos.y--;
@ -866,7 +872,7 @@ void ProtocolGame::parseOutfitWindow(InputMessage& msg)
} }
CreaturePtr creature = CreaturePtr(new Creature); CreaturePtr creature = CreaturePtr(new Creature);
creature->setXPattern(2); creature->setDirection(Otc::South);
creature->setOutfit(outfit); creature->setOutfit(outfit);
g_lua.callGlobalField("Game", "onOpenOutfitWindow", creature, outfitList); 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; int startz, endz, zstep, skip = 0;
if(z > 7) { if(z > Otc::SEA_FLOOR) {
startz = z - 2; startz = z - Otc::AWARE_UNDEGROUND_FLOOR_RANGE;
endz = (15 < z+2) ? 15 : z+2; endz = std::min(z + Otc::AWARE_UNDEGROUND_FLOOR_RANGE, (int)Otc::MAX_Z);
zstep = 1; zstep = 1;
} }
else { else {
startz = 7; startz = Otc::SEA_FLOOR;
endz = 0; endz = 0;
zstep = -1; 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 nx = 0; nx < width; nx++) {
for(int ny = 0; ny < height; ny++) { 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) { if(skip == 0) {
int tileOpt = msg.getU16(true); int tileOpt = msg.getU16(true);
if(tileOpt >= 0xFF00) if(tileOpt >= 0xFF00)
skip = (msg.getU16() & 0xFF); skip = (msg.getU16() & 0xFF);
else { else {
Position pos(x + nx + offset, y + ny + offset, z); setTileDescription(msg, tilePos);
setTileDescription(msg, pos);
skip = (msg.getU16() & 0xFF); skip = (msg.getU16() & 0xFF);
} }
} }
@ -1041,7 +1051,7 @@ ThingPtr ProtocolGame::internalGetThing(InputMessage& msg)
if(knownCreature) if(knownCreature)
creature = knownCreature; creature = knownCreature;
else 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 } else if(thingId == 0x0061) { //creature is not known
uint removeId = msg.getU32(); uint removeId = msg.getU32();
uint id = msg.getU32(); uint id = msg.getU32();
@ -1065,6 +1075,8 @@ ThingPtr ProtocolGame::internalGetThing(InputMessage& msg)
creature->setId(id); creature->setId(id);
creature->setName(name); creature->setName(name);
g_map.addCreature(creature);
} }
uint8 healthPercent = msg.getU8(); uint8 healthPercent = msg.getU8();
@ -1119,7 +1131,7 @@ ItemPtr ProtocolGame::internalGetItem(InputMessage& msg, int id)
ItemPtr item = Item::create(id); ItemPtr item = Item::create(id);
if(item->isStackable() || item->isFluidContainer() || item->isFluid()) if(item->isStackable() || item->isFluidContainer() || item->isFluid())
item->setData(msg.getU8()); item->setCountOrSubType(msg.getU8());
return item; return item;
} }

@ -341,24 +341,24 @@ void ProtocolGame::sendLookAt(const Position& position, int thingId, int stackpo
send(oMsg); 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) if(message.length() > 255 || message.length() <= 0)
return; return;
int speakType = Proto::translateSpeakTypeDesc(speakTypeDesc); int serverSpeakType = Proto::translateSpeakTypeToServer(speakType);
OutputMessage oMsg; OutputMessage oMsg;
oMsg.addU8(Proto::ClientTalk); oMsg.addU8(Proto::ClientTalk);
oMsg.addU8(speakType); oMsg.addU8(serverSpeakType);
switch(speakType) { switch(serverSpeakType) {
case Proto::SpeakPrivate: case Proto::ServerSpeakPrivate:
case Proto::SpeakPrivateRed: case Proto::ServerSpeakPrivateRed:
oMsg.addString(receiver); oMsg.addString(receiver);
break; break;
case Proto::SpeakChannelYellow: case Proto::ServerSpeakChannelYellow:
case Proto::SpeakChannelRed: case Proto::ServerSpeakChannelRed:
oMsg.addU16(channelId); oMsg.addU16(channelId);
break; break;
} }
@ -390,10 +390,10 @@ void ProtocolGame::sendLeaveChannel(int channelId)
send(oMsg); send(oMsg);
} }
void ProtocolGame::sendPrivateChannel(const std::string& receiver) void ProtocolGame::sendOpenPrivateChannel(const std::string& receiver)
{ {
OutputMessage oMsg; OutputMessage oMsg;
oMsg.addU8(Proto::ClientPrivateChannel); oMsg.addU8(Proto::ClientOpenPrivateChannel);
oMsg.addString(receiver); oMsg.addString(receiver);
send(oMsg); send(oMsg);
} }

@ -24,6 +24,7 @@
#include <framework/core/modulemanager.h> #include <framework/core/modulemanager.h>
#include "core/game.h" #include "core/game.h"
#include <framework/core/resourcemanager.h> #include <framework/core/resourcemanager.h>
#include "core/map.h"
OTClient::OTClient(const std::string& appName) : Application(appName) OTClient::OTClient(const std::string& appName) : Application(appName)
{ {
@ -40,6 +41,8 @@ void OTClient::init(const std::vector<std::string>& args)
g_modules.ensureModuleLoaded("client"); g_modules.ensureModuleLoaded("client");
g_modules.autoLoadModules(1000); g_modules.autoLoadModules(1000);
g_map.load();
// load otclientrc.lua // load otclientrc.lua
if(g_resources.fileExists("/otclientrc.lua")) { if(g_resources.fileExists("/otclientrc.lua")) {
try { try {

@ -30,7 +30,7 @@ void UICreature::draw()
if(m_creature) { if(m_creature) {
g_painter.setColor(Fw::white); 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(); drawChildren();

@ -32,6 +32,7 @@ bool UIGame::onKeyPress(uchar keyCode, int keyboardModifiers, bool wouldFilter)
UILineEditPtr chatLineEdit = std::dynamic_pointer_cast<UILineEdit>(getParent()->recursiveGetChildById("consoleLineEdit")); UILineEditPtr chatLineEdit = std::dynamic_pointer_cast<UILineEdit>(getParent()->recursiveGetChildById("consoleLineEdit"));
//TODO: move this whole shit to lua
if(keyboardModifiers == Fw::KeyboardNoModifier) { if(keyboardModifiers == Fw::KeyboardNoModifier) {
if(keyCode == Fw::KeyUp || keyCode == Fw::KeyNumpad8) { if(keyCode == Fw::KeyUp || keyCode == Fw::KeyNumpad8) {
g_game.walk(Otc::North); g_game.walk(Otc::North);

@ -38,10 +38,10 @@ void UIItem::draw()
Point topLeft = m_rect.bottomRight() - Point(32, 32) + Point(m_padding.left, m_padding.top); Point topLeft = m_rect.bottomRight() - Point(32, 32) + Point(m_padding.left, m_padding.top);
g_painter.setColor(Fw::white); 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) { if(m_font && m_item->isStackable() && m_item->getCount() > 1) {
std::string count = Fw::tostring(m_item->getData()); 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)); 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(); 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<int>());
else if(node->tag() == "item-count")
setItemCount(node->value<int>());
else if(node->tag() == "virtual")
setVirtual(node->value<bool>());
}
}

@ -33,12 +33,24 @@ public:
UIItem(); UIItem();
void draw(); 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 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; } ItemPtr getItem() { return m_item; }
bool isVirtual() { return m_virtual; }
protected: protected:
void onStyleApply(const std::string& styleName, const OTMLNodePtr& styleNode);
ItemPtr m_item; ItemPtr m_item;
Boolean<false> m_virtual;
}; };
#endif #endif

@ -23,6 +23,7 @@
#include "uimap.h" #include "uimap.h"
#include <otclient/core/game.h> #include <otclient/core/game.h>
#include <otclient/core/map.h> #include <otclient/core/map.h>
#include <otclient/core/mapview.h>
#include <framework/otml/otml.h> #include <framework/otml/otml.h>
#include <framework/graphics/graphics.h> #include <framework/graphics/graphics.h>
#include <otclient/core/localplayer.h> #include <otclient/core/localplayer.h>
@ -30,21 +31,79 @@
UIMap::UIMap() UIMap::UIMap()
{ {
m_dragable = true; 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() void UIMap::draw()
{ {
drawSelf(); drawSelf();
// draw map border
g_painter.setColor(Fw::black); g_painter.setColor(Fw::black);
g_painter.drawBoundingRect(m_mapRect.expanded(1)); g_painter.drawBoundingRect(m_mapRect.expanded(1));
g_map.draw(m_mapRect);
m_mapView->draw(m_mapRect);
drawChildren(); 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) TilePtr UIMap::getTile(const Point& mousePos)
{ {
/*
if(!m_mapRect.contains(mousePos)) if(!m_mapRect.contains(mousePos))
return nullptr; return nullptr;
@ -55,12 +114,12 @@ TilePtr UIMap::getTile(const Point& mousePos)
if(localPlayer) if(localPlayer)
relativeStretchMousePos += localPlayer->getWalkOffset(); 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 stretchFactor(m_mapRect.width() / (float)mapSize.width(), m_mapRect.height() / (float)mapSize.height());
PointF relativeMousePos = PointF(relativeStretchMousePos.x, relativeStretchMousePos.y) / stretchFactor; 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(); 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()) if(!tilePos.isValid())
return nullptr; 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 // We must check every floor, from top to bottom to check for a clickable tile
int firstFloor = g_map.getFirstVisibleFloor(); int firstFloor = g_map.getFirstVisibleFloor();
tilePos.perspectiveUp(tilePos.z - firstFloor); tilePos.coveredUp(tilePos.z - firstFloor);
for(int i = firstFloor; i <= Map::MAX_Z; i++) { for(int i = firstFloor; i <= Map::MAX_Z; i++) {
tile = g_map.getTile(tilePos); tile = g_map.getTile(tilePos);
if(tile && tile->isClickable()) if(tile && tile->isClickable())
@ -84,12 +143,14 @@ TilePtr UIMap::getTile(const Point& mousePos)
return nullptr; return nullptr;
return tile; return tile;
*/
return nullptr;
} }
void UIMap::onGeometryChange(const Rect& oldRect, const Rect& newRect) void UIMap::onGeometryChange(const Rect& oldRect, const Rect& newRect)
{ {
Rect mapRect = getChildrenRect().expanded(-1); 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); mapSize.scale(mapRect.size(), Fw::KeepAspectRatio);
m_mapRect.resize(mapSize); m_mapRect.resize(mapSize);

@ -31,14 +31,21 @@ class UIMap : public UIWidget
{ {
public: public:
UIMap(); UIMap();
~UIMap();
void draw(); void draw();
void zoomIn();
void zoomOut();
void setCameraPosition(const Position& pos);
TilePtr getTile(const Point& mousePos); TilePtr getTile(const Point& mousePos);
protected: protected:
virtual void onGeometryChange(const Rect& oldRect, const Rect& newRect); virtual void onGeometryChange(const Rect& oldRect, const Rect& newRect);
private: private:
MapViewPtr m_mapView;
Rect m_mapRect; Rect m_mapRect;
}; };

@ -30,51 +30,99 @@
class Position class Position
{ {
public: public:
Position() : x(-1), y(-1), z(-1) { } Position() : x(65535), y(65535), z(255) { }
Position(int x, int y, int z) : x(x), y(y), z(z) { } 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) { switch(direction) {
case Otc::North: case Otc::North:
return Position( 0, -1, 0); pos.y--;
break;
case Otc::East: case Otc::East:
return Position( 1, 0, 0); pos.x++;
break;
case Otc::South: case Otc::South:
return Position( 0, 1, 0); pos.y++;
break;
case Otc::West: case Otc::West:
return Position(-1, 0, 0); pos.x--;
break;
case Otc::NorthEast: case Otc::NorthEast:
return Position( 1, -1, 0); pos.x++;
pos.y--;
break;
case Otc::SouthEast: case Otc::SouthEast:
return Position( 1, 1, 0); pos.x++;
pos.y++;
break;
case Otc::SouthWest: case Otc::SouthWest:
return Position(-1, 1, 0); pos.x--;
pos.y++;
break;
case Otc::NorthWest: case Otc::NorthWest:
return Position(-1, -1, 0); pos.x--;
default: pos.y--;
return Position(); 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 { 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; return Otc::InvalidDirection;
else if(positionDelta.x == 0) { else if(dx == 0) {
if(positionDelta.y < 0) if(dy < 0)
return Otc::North; return Otc::North;
else if(positionDelta.y > 0) else if(dy > 0)
return Otc::South; return Otc::South;
} }
else if(positionDelta.y == 0) { else if(dy == 0) {
if(positionDelta.x < 0) if(dx < 0)
return Otc::West; return Otc::West;
else if(positionDelta.x > 0) else if(dx > 0)
return Otc::East; return Otc::East;
} }
else { 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) if(angle < 0)
angle += 360; angle += 360;
@ -98,7 +146,10 @@ public:
return Otc::InvalidDirection; 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) 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; } 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 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 { 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; }
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 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; } bool down(int n = 1) {
void down(int n = 1) { z+=n; } 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; } bool coveredUp(int n = 1) {
void coveredDown(int n = 1) { x-=n; y-=n; z+=n; } 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; uint16 x;
int y; uint16 y;
int z; uint8 z;
}; };
struct PositionHasher : std::unary_function<Position, std::size_t> { struct PositionHasher : std::unary_function<Position, std::size_t> {
@ -132,13 +214,17 @@ struct PositionHasher : std::unary_function<Position, std::size_t> {
inline std::ostream& operator<<(std::ostream& out, const Position& pos) 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; return out;
} }
inline std::istream& operator>>(std::istream& in, Position& pos) 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; return in;
} }

Loading…
Cancel
Save