merge revgraphics

This commit is contained in:
Eduardo Bart 2012-02-03 02:21:36 -02:00
commit 7bb828faee
95 changed files with 2652 additions and 1259 deletions

View File

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

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

View File

@ -298,3 +298,18 @@ KeyCodeDescs = {
[KeyNumpad8] = 'Numpad8',
[KeyNumpad9] = 'Numpad9'
}
SpeakSay = 1
SpeakWhisper = 2
SpeakYell = 3
SpeakBroadcast = 4
SpeakPrivate = 5
SpeakPrivateRed = 6
SpeakPrivatePlayerToNpc = 7
SpeakPrivateNpcToPlayer = 8
SpeakChannelYellow = 9
SpeakChannelWhite = 10
SpeakChannelRed = 11
SpeakChannelOrange = 12
SpeakMonsterSay = 13
SpeakMonsterYell = 14

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -26,20 +26,20 @@ function UIMap:onDrop(widget, mousePos)
local tile = self:getTile(mousePos)
if not tile then return false end
local data = widget.currentDragThing:getData()
local count = widget.currentDragThing:getCount()
if widget.currentDragThing:isStackable() and data > 1 then
widget.parsed = true
local moveWindow = displayUI('/game/movewindow.otui')
local spinbox = moveWindow:getChildById('spinbox')
spinbox:setMaximum(data)
spinbox:setMaximum(count)
spinbox:setMinimum(1)
spinbox:setCurrentIndex(data)
spinbox:setCurrentIndex(count)
local okButton = moveWindow:getChildById('buttonOk')
okButton.onClick = function() Game.move(widget.currentDragThing, tile:getPos(), spinbox:getCurrentIndex()) okButton:getParent():destroy() widget.currentDragThing = nil end
okButton.onClick = function() Game.move(widget.currentDragThing, tile:getPosition(), spinbox:getCurrentIndex()) okButton:getParent():destroy() widget.currentDragThing = nil end
moveWindow.onEnter = okButton.onClick
else
Game.move(widget.currentDragThing, tile:getPos(), 1)
Game.move(widget.currentDragThing, tile:getPosition(), 1)
end
return true

View File

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

View File

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

View File

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

View File

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

View File

@ -112,3 +112,22 @@ void ConfigManager::remove(const std::string& key)
if(child)
m_confsDoc->removeChild(child);
}
void ConfigManager::setNode(const std::string& key, const OTMLNodePtr& node)
{
remove(key);
addNode(key, node);
}
void ConfigManager::addNode(const std::string& key, const OTMLNodePtr& node)
{
OTMLNodePtr clone = node->clone();
node->setTag(key);
node->setUnique(true);
m_confsDoc->addChild(node);
}
OTMLNodePtr ConfigManager::getNode(const std::string& key)
{
return m_confsDoc->get(key);
}

View File

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

View File

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

View File

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

View File

@ -123,7 +123,7 @@ bool ResourceManager::saveFile(const std::string& fileName, const uchar* data, u
return true;
}
bool ResourceManager::saveFile(const std::string& fileName, std::istream& in)
bool ResourceManager::saveFile(const std::string& fileName, std::iostream& in)
{
std::streampos oldPos = in.tellg();
in.seekg(0, std::ios::end);

View File

@ -44,7 +44,7 @@ public:
bool saveFile(const std::string& fileName, const uchar* data, uint size);
bool saveFile(const std::string& fileName, const std::string& data);
bool saveFile(const std::string& fileName, std::istream& in);
bool saveFile(const std::string& fileName, std::iostream& in);
bool deleteFile(const std::string& fileName);

View File

@ -26,6 +26,15 @@
uint FrameBuffer::boundFbo = 0;
FrameBuffer::FrameBuffer()
{
m_clearColor = Fw::alpha;
glGenFramebuffers(1, &m_fbo);
if(!m_fbo)
logFatal("Unable to create framebuffer object");
}
FrameBuffer::FrameBuffer(const Size& size)
{
m_clearColor = Fw::alpha;
@ -44,6 +53,9 @@ FrameBuffer::~FrameBuffer()
void FrameBuffer::resize(const Size& size)
{
if(!size.isValid())
return;
if(m_texture && m_texture->getSize() == size)
return;
@ -83,6 +95,16 @@ void FrameBuffer::release()
g_graphics.setViewportSize(m_oldViewportSize);
}
void FrameBuffer::generateMipmaps()
{
m_texture->generateMipmaps();
}
void FrameBuffer::draw(const Rect& dest, const Rect& src)
{
g_painter.drawTexturedRect(dest, m_texture, src);
}
void FrameBuffer::draw(const Rect& dest)
{
g_painter.drawTexturedRect(dest, m_texture);

View File

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

View File

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

View File

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

View File

@ -60,8 +60,8 @@ void Painter::drawProgram(const PainterShaderProgramPtr& program, CoordsBuffer&
return;
program->setProjectionMatrix(m_projectionMatrix);
program->setOpacity(m_currentOpacity);
program->setColor(m_currentColor);
program->setOpacity(m_opacity);
program->setColor(m_color);
program->draw(coordsBuffer, drawMode);
}
@ -141,4 +141,32 @@ void Painter::setCompositionMode(Painter::CompositionMode compositionMode)
glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA);
break;
}
m_compostionMode = compositionMode;
}
void Painter::saveAndResetState()
{
m_oldCustomProgram = m_customProgram;
m_oldProjectionMatrix = m_projectionMatrix;
m_oldColor = m_color;
m_oldOpacity = m_opacity;
m_oldCompostionMode = m_compostionMode;
releaseCustomProgram();
setColor(Fw::white);
setOpacity(1);
setCompositionMode(CompositionMode_Normal);
}
void Painter::restoreSavedState()
{
setCustomProgram(m_oldCustomProgram);
setColor(m_oldColor);
setOpacity(m_oldOpacity);
setCompositionMode(m_oldCompostionMode);
m_oldCustomProgram = nullptr;
m_oldColor = Fw::white;
m_oldOpacity = 1;
m_oldCompostionMode = CompositionMode_Normal;
}

View File

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

View File

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

View File

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

View File

@ -29,7 +29,7 @@ Particle::Particle(const Point& pos, const Size& startSize, const Size& finalSiz
m_colors = colors;
m_colorsStops = colorsStops;
m_pos = PointF(pos.x, pos.y);
m_position = PointF(pos.x, pos.y);
m_startSize = startSize;
m_finalSize = finalSize;
m_velocity = velocity;
@ -80,18 +80,18 @@ void Particle::updatePosition(double elapsedTime)
PointF delta = m_velocity * elapsedTime;
delta.y *= -1; // painter orientate Y axis in the inverse direction
PointF position = m_pos + delta;
PointF position = m_position + delta;
if(m_pos != position) {
if(m_position != position) {
mustRedraw = true;
m_pos += delta;
m_position += delta;
}
// update acceleration
m_velocity += m_acceleration * elapsedTime;
}
m_rect.move((int)m_pos.x - m_size.width() / 2, (int)m_pos.y - m_size.height() / 2);
m_rect.move((int)m_position.x - m_size.width() / 2, (int)m_position.y - m_size.height() / 2);
}
void Particle::updateSize()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -89,39 +89,127 @@ uint Texture::internalLoadGLTexture(uchar *pixels, int channels, int width, int
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// nearest filtering (non smooth)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
setupFilters();
return id;
}
void Texture::generateMipmaps()
{
bind();
if(!m_useMipmaps) {
m_useMipmaps = true;
setupFilters();
}
glGenerateMipmap(GL_TEXTURE_2D);
}
void Texture::setSmooth(bool smooth)
{
if(smooth == m_smooth)
return;
if(smooth) {
// enable smooth texture
glBindTexture(GL_TEXTURE_2D, m_textureId);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
} else {
// nearest filtering (non smooth)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
m_smooth = true;
m_smooth = smooth;
bind();
setupFilters();
}
std::vector<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);
#ifdef OPENGL_ES
// hack to copy pixels from opengl memory in opengl es
// NOTE: this can be slow, but its the only way to get pixels from a texture in OpenGL ES
FrameBufferPtr fb(new FrameBuffer(m_size));
fb->bind();
g_painter.saveAndResetState();
g_painter.drawTexturedRect(Rect(0,0,m_size), shared_from_this());
glReadPixels(0, 0, m_size.width(), m_size.height(), GL_RGBA, GL_UNSIGNED_BYTE, &pixels[0]);
g_painter.restoreSavedState();
fb->release();
#else
// copy pixels from opengl memory
glBindTexture(GL_TEXTURE_2D, m_textureId);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, &pixels[0]);
#endif
return pixels;
}
void Texture::generateBilinearMipmaps(std::vector<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);
}

View File

@ -32,7 +32,14 @@ public:
Texture(int width, int height, int channels, uchar* pixels = NULL);
virtual ~Texture();
virtual void setSmooth(bool smooth);
void bind() { glBindTexture(GL_TEXTURE_2D, m_textureId); }
void generateMipmaps();
// generate bilinear mipmaps optimized for alpha textures
void generateBilinearMipmaps(std::vector<uint8> inPixels);
void setSmooth(bool smooth);
GLuint getId() { return m_textureId; }
std::vector<uint8> getPixels();
@ -44,10 +51,12 @@ public:
bool isEmpty() const { return m_textureId == 0; }
protected:
void setupFilters();
GLuint internalLoadGLTexture(uchar* pixels, int channels, int w, int h);
GLuint m_textureId;
Size m_size;
Boolean<false> m_useMipmaps;
Boolean<false> m_smooth;
};

View File

@ -154,7 +154,7 @@ void Application::registerLuaFunctions()
g_lua.bindClassMemberFunction<UIWidget>("setWidth", &UIWidget::setWidth);
g_lua.bindClassMemberFunction<UIWidget>("setHeight", &UIWidget::setHeight);
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>("setBackgroundColor", &UIWidget::setBackgroundColor);
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>("getX", &UIWidget::getX);
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>("getHeight", &UIWidget::getHeight);
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", "exists", std::bind(&ConfigManager::exists, &g_configs, _1));
g_lua.bindClassStaticFunction("g_configs", "remove", std::bind(&ConfigManager::remove, &g_configs, _1));
g_lua.bindClassStaticFunction("g_configs", "setNode", std::bind(&ConfigManager::setNode, &g_configs, _1, _2));
g_lua.bindClassStaticFunction("g_configs", "addNode", std::bind(&ConfigManager::addNode, &g_configs, _1, _2));
g_lua.bindClassStaticFunction("g_configs", "getNode", std::bind(&ConfigManager::getNode, &g_configs, _1));
// PlatformWindow
g_lua.registerStaticClass("g_window");
@ -409,7 +412,7 @@ void Application::registerLuaFunctions()
g_lua.bindClassStaticFunction("g_window", "getWidth", std::bind(&PlatformWindow::getWidth, &g_window));
g_lua.bindClassStaticFunction("g_window", "getHeight", std::bind(&PlatformWindow::getHeight, &g_window));
g_lua.bindClassStaticFunction("g_window", "getUnmaximizedPos", std::bind(&PlatformWindow::getUnmaximizedPos, &g_window));
g_lua.bindClassStaticFunction("g_window", "getPos", std::bind(&PlatformWindow::getPos, &g_window));
g_lua.bindClassStaticFunction("g_window", "getPosition", std::bind(&PlatformWindow::getPosition, &g_window));
g_lua.bindClassStaticFunction("g_window", "getX", std::bind(&PlatformWindow::getX, &g_window));
g_lua.bindClassStaticFunction("g_window", "getY", std::bind(&PlatformWindow::getY, &g_window));
g_lua.bindClassStaticFunction("g_window", "getMousePos", std::bind(&PlatformWindow::getMousePos, &g_window));

View File

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

View File

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

View File

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

View File

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

View File

@ -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) 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) 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) { 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) { (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; }
bool operator==(const TSize<T>& other) const { return other.wd==wd && other.ht==ht; }

View File

@ -35,6 +35,7 @@ Connection::Connection() :
{
m_connected = false;
m_connecting = false;
m_sendBufferSize = 0;
}
Connection::~Connection()
@ -57,6 +58,7 @@ void Connection::connect(const std::string& host, uint16 port, const SimpleCallb
{
m_connected = false;
m_connecting = true;
m_error.clear();
m_connectCallback = connectCallback;
asio::ip::tcp::resolver::query query(host, Fw::unsafeCast<std::string>(port));
@ -85,6 +87,10 @@ void Connection::close()
if(!m_connected && !m_connecting)
return;
// flush send data before disconnecting on clean connections
if(m_connected && !m_error && m_sendBufferSize > 0 && m_sendEvent)
m_sendEvent->execute();
m_connecting = false;
m_connected = false;
m_connectCallback = nullptr;
@ -104,17 +110,34 @@ void Connection::close()
void Connection::write(uint8* buffer, uint16 size)
{
m_writeTimer.cancel();
if(!m_connected)
return;
// send old buffer if we can't add more data
if(m_sendBufferSize + size >= SEND_BUFFER_SIZE && m_sendEvent)
m_sendEvent->execute();
// we can't send the data right away, otherwise we could create tcp congestion
memcpy(m_sendBuffer + m_sendBufferSize, buffer, size);
m_sendBufferSize += size;
if(!m_sendEvent || m_sendEvent->isExecuted() || m_sendEvent->isCanceled()) {
auto weakSelf = ConnectionWeakPtr(shared_from_this());
m_sendEvent = g_dispatcher.scheduleEvent([=] {
if(!weakSelf.lock())
return;
//m_writeTimer.cancel();
asio::async_write(m_socket,
asio::buffer(buffer, size),
asio::buffer(m_sendBuffer, m_sendBufferSize),
std::bind(&Connection::onWrite, shared_from_this(), _1, _2));
m_writeTimer.expires_from_now(boost::posix_time::seconds(WRITE_TIMEOUT));
m_writeTimer.async_wait(std::bind(&Connection::onTimeout, shared_from_this(), _1));
m_sendBufferSize = 0;
}, SEND_INTERVAL);
}
}
void Connection::read(uint16 bytes, const RecvCallback& callback)
@ -157,7 +180,7 @@ void Connection::onConnect(const boost::system::error_code& error)
if(!error) {
m_connected = true;
// disable nagle's algorithm
// disable nagle's algorithm, this make the game play smoother
boost::asio::ip::tcp::no_delay option(true);
m_socket.set_option(option);
@ -203,6 +226,7 @@ void Connection::onTimeout(const boost::system::error_code& error)
void Connection::handleError(const boost::system::error_code& error)
{
if(error != asio::error::operation_aborted) {
m_error = error;
if(m_errorCallback)
m_errorCallback(error);
if(m_connected || m_connecting)

View File

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

View File

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

View File

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

View File

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

View File

@ -278,7 +278,7 @@ void WIN32Window::internalCreateWindow()
DWORD dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
DWORD dwStyle = WS_OVERLAPPEDWINDOW;
RECT windowRect = {m_pos.x, m_pos.y, m_pos.x + m_size.width(), m_pos.y + m_size.height()};
RECT windowRect = {m_position.x, m_position.y, m_position.x + m_size.width(), m_position.y + m_size.height()};
AdjustWindowRectEx(&windowRect, dwStyle, FALSE, dwExStyle);
updateUnmaximizedCoords();
@ -361,14 +361,14 @@ void *WIN32Window::getExtensionProcAddress(const char *ext)
void WIN32Window::move(const Point& pos)
{
RECT windowRect = {pos.x, pos.y, m_pos.x + m_size.width(), m_pos.y + m_size.height()};
RECT windowRect = {pos.x, pos.y, m_position.x + m_size.width(), m_position.y + m_size.height()};
AdjustWindowRectEx(&windowRect, WS_OVERLAPPEDWINDOW, FALSE, WS_EX_APPWINDOW | WS_EX_WINDOWEDGE);
MoveWindow(m_window, windowRect.left, windowRect.top, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, TRUE);
}
void WIN32Window::resize(const Size& size)
{
RECT windowRect = {m_pos.x, m_pos.y, m_pos.x + size.width(), m_pos.y + size.height()};
RECT windowRect = {m_position.x, m_position.y, m_position.x + size.width(), m_position.y + size.height()};
AdjustWindowRectEx(&windowRect, WS_OVERLAPPEDWINDOW, FALSE, WS_EX_APPWINDOW | WS_EX_WINDOWEDGE);
MoveWindow(m_window, windowRect.left, windowRect.top, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, TRUE);
}
@ -503,8 +503,8 @@ LRESULT WIN32Window::windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar
break;
}
case WM_MOVE: {
m_pos.x = LOWORD(lParam);
m_pos.y = HIWORD(lParam);
m_position.x = LOWORD(lParam);
m_position.y = HIWORD(lParam);
break;
}
case WM_SIZE: {

View File

@ -284,7 +284,7 @@ void X11Window::internalCreateWindow()
updateUnmaximizedCoords();
m_window = XCreateWindow(m_display, m_rootWindow,
m_pos.x, m_pos.y, m_size.width(), m_size.height(),
m_position.x, m_position.y, m_size.width(), m_size.height(),
0,
depth,
InputOutput,
@ -300,7 +300,7 @@ void X11Window::internalCreateWindow()
XSetWMHints(m_display, m_window, &hints);
// ensure window position
XMoveWindow(m_display, m_window, m_pos.x, m_pos.y);
XMoveWindow(m_display, m_window, m_position.x, m_position.y);
// handle wm_delete events
m_wmDelete = XInternAtom(m_display, "WM_DELETE_WINDOW", True);
@ -580,7 +580,7 @@ void X11Window::poll()
}
// updates window pos
m_pos = newPos;
m_position = newPos;
updateUnmaximizedCoords();
break;
}

View File

@ -90,7 +90,7 @@ void UIWidget::drawChildren()
g_painter.setColor(Fw::green);
g_painter.drawBoundingRect(child->getRect());
}
//g_fonts.getDefaultFont()->renderText(child->getId(), child->getPos() + Point(2, 0), Fw::red);
//g_fonts.getDefaultFont()->renderText(child->getId(), child->getPosition() + Point(2, 0), Fw::red);
g_painter.setOpacity(oldOpacity);
}

View File

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

View File

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

View File

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

View File

@ -27,6 +27,7 @@ SET(otclient_SOURCES ${otclient_SOURCES}
# otclient core
${CMAKE_CURRENT_LIST_DIR}/core/game.cpp
${CMAKE_CURRENT_LIST_DIR}/core/map.cpp
${CMAKE_CURRENT_LIST_DIR}/core/mapview.cpp
${CMAKE_CURRENT_LIST_DIR}/core/thingstype.cpp
${CMAKE_CURRENT_LIST_DIR}/core/spritemanager.cpp
${CMAKE_CURRENT_LIST_DIR}/core/item.cpp

View File

@ -31,6 +31,51 @@ namespace Otc
static const char* AppCompactName = "otclient";
static const char* AppVersion = "0.4.0";
enum {
TILE_PIXELS = 32,
MAX_ELEVATION = 24,
SEA_FLOOR = 7,
MAX_Z = 15,
UNDERGROUND_FLOOR = SEA_FLOOR+1,
VISIBLE_X_TILES = 15,
VISIBLE_Y_TILES = 11,
AWARE_UNDEGROUND_FLOOR_RANGE = 2,
AWARE_X_TILES = VISIBLE_X_TILES + 3,
AWARE_Y_TILES = VISIBLE_Y_TILES + 3,
AWARE_X_LEFT_TILES = AWARE_X_TILES/2 - 1,
AWARE_X_RIGHT_TILES = AWARE_X_TILES/2,
AWARE_Y_TOP_TILES = AWARE_Y_TILES/2 - 1,
AWARE_Y_BOTTOM_TILES = AWARE_Y_TILES/2,
EFFECT_TICKS_PER_FRAME = 75,
INVISIBLE_TICKS_PER_FRAME = 500,
ITEM_TICKS_PER_FRAME = 500,
ANIMATED_TEXT_DURATION = 1000,
STATIC_DURATION_PER_CHARACTER = 75,
MIN_STATIC_TEXT_DURATION = 3000,
MAX_STATIC_TEXT_WIDTH = 200,
};
enum DrawFlags {
DrawGround = 1,
DrawGroundBorders = 2,
DrawOnBottom = 4,
DrawOnTop = 8,
DrawItems = 16,
DrawCreatures = 32,
DrawEffects = 64,
DrawMissiles = 128,
DrawCreaturesInformation = 256,
DrawStaticTexts = 512,
DrawAnimatedTexts = 1024,
DrawAnimations = 2048,
DrawWalls = DrawOnBottom | DrawOnTop,
DrawEverything = DrawGround | DrawGroundBorders | DrawWalls | DrawItems |
DrawCreatures | DrawEffects | DrawMissiles |
DrawCreaturesInformation | DrawStaticTexts | DrawAnimatedTexts | DrawAnimations
};
enum DatOpts {
DatGround = 0,
DatGroundClip,
@ -219,6 +264,23 @@ namespace Otc
IconPzBlock = 8192,
IconPz = 16384
};
enum SpeakType {
SpeakSay = 1,
SpeakWhisper,
SpeakYell,
SpeakBroadcast,
SpeakPrivate,
SpeakPrivateRed,
SpeakPrivatePlayerToNpc,
SpeakPrivateNpcToPlayer,
SpeakChannelYellow,
SpeakChannelWhite,
SpeakChannelRed,
SpeakChannelOrange,
SpeakMonsterSay,
SpeakMonsterYell
};
}
#endif

View File

@ -31,27 +31,28 @@ AnimatedText::AnimatedText()
m_font = g_fonts.getFont("verdana-11px-rounded");
}
void AnimatedText::start()
void AnimatedText::draw(const Point& dest, const Rect& visibleRect)
{
m_startTime = g_clock.time();
Point p = dest;
p.x += 20 - m_textSize.width() / 2;
p.y += (-20 * m_animationTimer.ticksElapsed()) / Otc::ANIMATED_TEXT_DURATION;
Rect rect(p, m_textSize);
auto self = asAnimatedText();
// schedule removal
g_dispatcher.scheduleEvent([self]() {
g_map.removeThing(self);
}, DURATION);
}
void AnimatedText::draw(const Point& p, const Rect& visibleRect)
{
if(m_font) {
Rect rect = Rect(p + Point(20 - m_textSize.width() / 2, -20.0 * g_clock.timeElapsed(m_startTime) / (DURATION / 1000)), m_textSize);
if(visibleRect.contains(rect))
if(visibleRect.contains(rect)) {
//TODO: cache into a framebuffer
m_font->renderText(m_text, rect, Fw::AlignLeft, m_color);
}
}
void AnimatedText::startAnimation()
{
m_animationTimer.restart();
// schedule removal
auto self = asAnimatedText();
g_dispatcher.scheduleEvent([self]() { g_map.removeThing(self); }, Otc::ANIMATED_TEXT_DURATION);
}
void AnimatedText::setColor(int color)
{
m_color = Color::from8bit(color);
@ -59,7 +60,6 @@ void AnimatedText::setColor(int color)
void AnimatedText::setText(const std::string& text)
{
if(m_font)
m_textSize = m_font->calculateTextRectSize(text);
m_text = text;
}

View File

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

View File

@ -44,6 +44,7 @@ Creature::Creature() : Thing()
m_showVolatileSquare = false;
m_showStaticSquare = false;
m_direction = Otc::South;
m_walkAnimationPhase = 0;
m_walking = false;
m_walkInterval = 0;
m_walkAnimationInterval = 0;
@ -63,16 +64,18 @@ int LEGS_COLOR_UNIFORM = 12;
int FEET_COLOR_UNIFORM = 13;
int MASK_TEXTURE_UNIFORM = 14;
void Creature::draw(const Point& p, const Rect&)
void Creature::draw(const Point& dest, float scaleFactor, bool animate)
{
if(m_showVolatileSquare) {
Point animationOffset = animate ? m_walkOffset : Point(0,0);
if(m_showVolatileSquare && animate) {
g_painter.setColor(m_volatileSquareColor);
g_painter.drawBoundingRect(Rect(p + m_walkOffset - Point(m_type->parameters[ThingType::DisplacementX], m_type->parameters[ThingType::DisplacementY]) + 3, Size(28, 28)), 2);
g_painter.drawBoundingRect(Rect(dest + (animationOffset - getDisplacement() + 3)*scaleFactor, Size(28, 28)*scaleFactor), std::max((int)(2*scaleFactor), 1));
}
if(m_showStaticSquare) {
if(m_showStaticSquare && animate) {
g_painter.setColor(m_staticSquareColor);
g_painter.drawBoundingRect(Rect(p + m_walkOffset - Point(m_type->parameters[ThingType::DisplacementX], m_type->parameters[ThingType::DisplacementY]) + 1, Size(32, 32)), 2);
g_painter.drawBoundingRect(Rect(dest + (animationOffset - getDisplacement() + 1)*scaleFactor, Size(Otc::TILE_PIXELS, Otc::TILE_PIXELS)*scaleFactor), std::max((int)(2*scaleFactor), 1));
}
g_painter.setColor(Fw::white);
@ -88,12 +91,29 @@ void Creature::draw(const Point& p, const Rect&)
outfitProgram->bindUniformLocation(MASK_TEXTURE_UNIFORM, "maskTexture");
}
// Render creature
int xPattern = 0, yPattern = 0, zPattern = 0;
// outfit is a real creature
if(m_outfit.getCategory() == ThingsType::Creature) {
for(m_yPattern = 0; m_yPattern < m_type->dimensions[ThingType::PatternY]; m_yPattern++) {
int animationPhase = animate ? m_walkAnimationPhase : 0;
if(isAnimateAlways()) {
int ticksPerFrame = 1000 / getAnimationPhases();
animationPhase = (g_clock.ticks() % (ticksPerFrame * getAnimationPhases())) / ticksPerFrame;
}
// xPattern => creature direction
if(m_direction == Otc::NorthEast || m_direction == Otc::SouthEast)
xPattern = Otc::East;
else if(m_direction == Otc::NorthWest || m_direction == Otc::SouthWest)
xPattern = Otc::West;
else
xPattern = m_direction;
// yPattern => creature addon
for(yPattern = 0; yPattern < getNumPatternsY(); yPattern++) {
// continue if we dont have this addon.
if(m_yPattern > 0 && !(m_outfit.getAddons() & (1 << (m_yPattern-1))))
if(yPattern > 0 && !(m_outfit.getAddons() & (1 << (yPattern-1))))
continue;
g_painter.setCustomProgram(outfitProgram);
@ -104,40 +124,51 @@ void Creature::draw(const Point& p, const Rect&)
outfitProgram->setUniformValue(LEGS_COLOR_UNIFORM, m_outfit.getLegsColor());
outfitProgram->setUniformValue(FEET_COLOR_UNIFORM, m_outfit.getFeetColor());
for(int h = 0; h < m_type->dimensions[ThingType::Height]; h++) {
for(int w = 0; w < m_type->dimensions[ThingType::Width]; w++) {
int spriteId = m_type->getSpriteId(w, h, 0, m_xPattern, m_yPattern, m_zPattern, m_animation);
if(!spriteId)
continue;
TexturePtr spriteTex = g_sprites.getSpriteTexture(spriteId);
if(!spriteTex)
continue;
if(m_type->dimensions[ThingType::Layers] > 1) {
int maskId = m_type->getSpriteId(w, h, 1, m_xPattern, m_yPattern, m_zPattern, m_animation);
TexturePtr maskTex = g_sprites.getSpriteTexture(maskId);
outfitProgram->setUniformTexture(MASK_TEXTURE_UNIFORM, maskTex, 1);
for(int h = 0; h < getDimensionHeight(); h++) {
for(int w = 0; w < getDimensionWidth(); w++) {
// setup texture outfit mask
TexturePtr maskTex;
if(getLayers() > 1) {
int maskId = getSpriteId(w, h, 1, xPattern, yPattern, zPattern, m_walkAnimationPhase);
maskTex = g_sprites.getSpriteTexture(maskId);
}
outfitProgram->setUniformTexture(MASK_TEXTURE_UNIFORM, maskTex, 1);
Rect drawRect(((p + m_walkOffset).x - w*32) - m_type->parameters[ThingType::DisplacementX],
((p + m_walkOffset).y - h*32) - m_type->parameters[ThingType::DisplacementY],
32, 32);
g_painter.drawTexturedRect(drawRect, spriteTex);
internalDraw(dest + (animationOffset - Point(w,h)*Otc::TILE_PIXELS)*scaleFactor,
scaleFactor, w, h, xPattern, yPattern, zPattern, 0, animationPhase);
}
}
g_painter.releaseCustomProgram();
}
// outfit is a creature imitating an item or the invisible effect
} else {
int animationPhase = 0;
int animationPhases = getAnimationPhases();
int animateTicks = Otc::ITEM_TICKS_PER_FRAME;
// when creature is an effect we cant render the first and last animation phase,
// instead we should loop in the phases between
if(m_outfit.getCategory() == ThingsType::Effect) {
animationPhases = std::max(1, animationPhases-2);
animateTicks = Otc::INVISIBLE_TICKS_PER_FRAME;
}
else if(m_outfit.getCategory() == ThingsType::Item) {
for(int l = 0; l < m_type->dimensions[ThingType::Layers]; l++)
internalDraw(p + m_walkOffset, l);
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::Effect)
internalDraw(p + m_walkOffset, 0);
}
void Creature::drawInformation(int x, int y, bool useGray, const Rect& visibleRect)
void Creature::drawInformation(const Point& point, bool useGray, const Rect& parentRect)
{
Color fillColor = Color(96, 96, 96);
@ -145,16 +176,16 @@ void Creature::drawInformation(int x, int y, bool useGray, const Rect& visibleRe
fillColor = m_informationColor;
// calculate main rects
Rect backgroundRect = Rect(x-(13.5), y, 27, 4);
backgroundRect.bind(visibleRect);
Rect backgroundRect = Rect(point.x-(13.5), point.y, 27, 4);
backgroundRect.bind(parentRect);
Rect textRect = Rect(x - m_nameSize.width() / 2.0, y-12, m_nameSize);
textRect.bind(visibleRect);
Rect textRect = Rect(point.x - m_nameSize.width() / 2.0, point.y-12, m_nameSize);
textRect.bind(parentRect);
// distance them
if(textRect.top() == visibleRect.top())
if(textRect.top() == parentRect.top())
backgroundRect.moveTop(textRect.top() + 12);
if(backgroundRect.bottom() == visibleRect.bottom())
if(backgroundRect.bottom() == parentRect.bottom())
textRect.moveTop(backgroundRect.top() - 12);
// health rect is based on background rect, so no worries
@ -173,15 +204,15 @@ void Creature::drawInformation(int x, int y, bool useGray, const Rect& visibleRe
if(m_skull != Otc::SkullNone && m_skullTexture) {
g_painter.setColor(Fw::white);
g_painter.drawTexturedRect(Rect(x + 12, y + 5, m_skullTexture->getSize()), m_skullTexture);
g_painter.drawTexturedRect(Rect(point.x + 12, point.y + 5, m_skullTexture->getSize()), m_skullTexture);
}
if(m_shield != Otc::ShieldNone && m_shieldTexture && m_showShieldTexture) {
g_painter.setColor(Fw::white);
g_painter.drawTexturedRect(Rect(x, y + 5, m_shieldTexture->getSize()), m_shieldTexture);
g_painter.drawTexturedRect(Rect(point.x, point.y + 5, m_shieldTexture->getSize()), m_shieldTexture);
}
if(m_emblem != Otc::EmblemNone && m_emblemTexture) {
g_painter.setColor(Fw::white);
g_painter.drawTexturedRect(Rect(x + 12, y + 16, m_emblemTexture->getSize()), m_emblemTexture);
g_painter.drawTexturedRect(Rect(point.x + 12, point.y + 16, m_emblemTexture->getSize()), m_emblemTexture);
}
}
@ -209,7 +240,12 @@ void Creature::walk(const Position& oldPos, const Position& newPos)
// calculates walk interval
float interval = 1000;
int groundSpeed = g_map.getTile(oldPos)->getGroundSpeed();
int groundSpeed = 0;
TilePtr oldTile = g_map.getTile(oldPos);
if(oldTile)
groundSpeed = oldTile->getGroundSpeed();
if(groundSpeed != 0)
interval = (1000.0f * groundSpeed) / m_speed;
@ -236,7 +272,7 @@ void Creature::stopWalk()
// reset walk animation states
m_walkOffset = Point(0,0);
m_animation = 0;
m_walkAnimationPhase = 0;
// stops the walk right away
terminateWalk();
@ -248,10 +284,10 @@ void Creature::updateWalkAnimation(int totalPixelsWalked)
if(m_outfit.getCategory() != ThingsType::Creature)
return;
if(totalPixelsWalked == 32 || totalPixelsWalked == 0 || m_type->dimensions[ThingType::AnimationPhases] <= 1)
m_animation = 0;
else if(m_type->dimensions[ThingType::AnimationPhases] > 1)
m_animation = 1 + ((totalPixelsWalked * 4) / Map::NUM_TILE_PIXELS) % (m_type->dimensions[ThingType::AnimationPhases] - 1);
if(totalPixelsWalked == 32 || totalPixelsWalked == 0 || getAnimationPhases() <= 1)
m_walkAnimationPhase = 0;
else if(getAnimationPhases() > 1)
m_walkAnimationPhase = 1 + ((totalPixelsWalked * 4) / Otc::TILE_PIXELS) % (getAnimationPhases() - 1);
}
void Creature::updateWalkOffset(int totalPixelsWalked)
@ -268,6 +304,36 @@ void Creature::updateWalkOffset(int totalPixelsWalked)
m_walkOffset.x = 32 - totalPixelsWalked;
}
void Creature::updateWalkingTile()
{
// determine new walking tile
TilePtr newWalkingTile;
Rect virtualCreatureRect(Otc::TILE_PIXELS + (m_walkOffset.x - getDisplacementX()),
Otc::TILE_PIXELS + (m_walkOffset.y - getDisplacementY()),
Otc::TILE_PIXELS, Otc::TILE_PIXELS);
for(int xi = -1; xi <= 1 && !newWalkingTile; ++xi) {
for(int yi = -1; yi <= 1 && !newWalkingTile; ++yi) {
Rect virtualTileRect((xi+1)*Otc::TILE_PIXELS, (yi+1)*Otc::TILE_PIXELS, Otc::TILE_PIXELS, Otc::TILE_PIXELS);
// only render creatures where bottom right is inside tile rect
if(virtualTileRect.contains(virtualCreatureRect.bottomRight())) {
const TilePtr& tile = g_map.getTile(m_position.translated(xi, yi, 0));
if(!tile)
continue;
newWalkingTile = tile;
}
}
}
if(newWalkingTile != m_walkingTile) {
if(m_walkingTile)
m_walkingTile->removeWalkingCreature(asCreature());
if(newWalkingTile)
newWalkingTile->addWalkingCreature(asCreature());
m_walkingTile = newWalkingTile;
}
}
void Creature::nextWalkUpdate()
{
// remove any previous scheduled walk updates
@ -295,6 +361,7 @@ void Creature::updateWalk()
// update walk animation and offsets
updateWalkAnimation(totalPixelsWalked);
updateWalkOffset(totalPixelsWalked);
updateWalkingTile();
// terminate walk
if(m_walking && m_walkTimer.ticksElapsed() >= m_walkInterval)
@ -315,6 +382,11 @@ void Creature::terminateWalk()
m_walkTurnDirection = Otc::InvalidDirection;
}
if(m_walkingTile) {
m_walkingTile->removeWalkingCreature(asCreature());
m_walkingTile = nullptr;
}
m_walking = false;
}
@ -358,40 +430,13 @@ void Creature::setHealthPercent(uint8 healthPercent)
void Creature::setDirection(Otc::Direction direction)
{
if(m_outfit.getCategory() == ThingsType::Creature) {
if(direction == Otc::NorthEast || direction == Otc::SouthEast)
m_xPattern = Otc::East;
else if(direction == Otc::NorthWest || direction == Otc::SouthWest)
m_xPattern = Otc::West;
else
m_xPattern = direction;
} else {
m_xPattern = 0;
}
m_direction = direction;
}
void Creature::setOutfit(const Outfit& outfit)
{
m_outfit = outfit;
m_type = getType();
m_animation = 0;
if(m_outfit.getCategory() == ThingsType::Effect) {
updateInvisibleAnimation();
m_xPattern = 0;
m_yPattern = 0;
}
if(m_outfit.getCategory() == ThingsType::Item) {
m_xPattern = 0;
m_yPattern = 0;
}
if(m_outfit.getCategory() == ThingsType::Creature && m_type->dimensions[ThingType::Layers] == 1) {
m_outfit.resetClothes();
}
m_type = g_thingsType.getThingType(outfit.getId(), outfit.getCategory());
}
void Creature::setSkull(uint8 skull)
@ -449,26 +494,6 @@ void Creature::addVolatileSquare(uint8 color)
}, VOLATILE_SQUARE_DURATION);
}
void Creature::updateInvisibleAnimation()
{
if(!g_game.isOnline() || m_outfit.getCategory() != ThingsType::Effect)
return;
if(m_animation == 1)
m_animation = 2;
else if(m_animation == 2)
m_animation = 3;
else if(m_animation == 3)
m_animation = 1;
else
m_animation = 1;
auto self = asCreature();
g_dispatcher.scheduleEvent([self]() {
self->updateInvisibleAnimation();
}, INVISIBLE_TICKS);
}
void Creature::updateShield()
{
m_showShieldTexture = !m_showShieldTexture;
@ -483,8 +508,17 @@ void Creature::updateShield()
m_showShieldTexture = true;
}
ThingType *Creature::getType()
Point Creature::getDrawOffset()
{
return g_thingsType.getThingType(m_outfit.getId(), m_outfit.getCategory());
Point drawOffset;
if(m_walking) {
if(m_walkingTile)
drawOffset -= Point(1,1) * m_walkingTile->getDrawElevation();
drawOffset += m_walkOffset;
} else {
const TilePtr& tile = getTile();
if(tile)
drawOffset -= Point(1,1) * tile->getDrawElevation();
}
return drawOffset;
}

View File

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

View File

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

View File

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

View File

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

View File

@ -94,7 +94,7 @@ void Game::processLogout()
m_protocolGame = nullptr;
}
g_map.clean();
g_map.save();
}
void Game::processDeath()
@ -172,9 +172,9 @@ void Game::processTextMessage(const std::string& type, const std::string& messag
g_lua.callGlobalField("Game","onTextMessage", type, message);
}
void Game::processCreatureSpeak(const std::string& name, int level, const std::string& type, const std::string& message, int channelId, const Position& creaturePos)
void Game::processCreatureSpeak(const std::string& name, int level, Otc::SpeakType type, const std::string& message, int channelId, const Position& creaturePos)
{
if(creaturePos.isValid() && (type == "say" || type == "whisper" || type == "yell" || type == "monsterSay" || type == "monsterYell")) {
if(creaturePos.isValid() && (type == Otc::SpeakSay || type == Otc::SpeakWhisper || type == Otc::SpeakYell || type == Otc::SpeakMonsterSay || type == Otc::SpeakMonsterYell)) {
StaticTextPtr staticText = StaticTextPtr(new StaticText);
staticText->addMessage(name, type, message);
g_map.addThing(staticText, creaturePos);
@ -186,7 +186,7 @@ void Game::processCreatureSpeak(const std::string& name, int level, const std::s
void Game::processContainerAddItem(int containerId, const ItemPtr& item)
{
if(item)
item->setPos(Position(65535, containerId + 0x40, 0));
item->setPosition(Position(65535, containerId + 0x40, 0));
g_lua.callGlobalField("Game", "onContainerAddItem", containerId, item);
}
@ -194,7 +194,7 @@ void Game::processContainerAddItem(int containerId, const ItemPtr& item)
void Game::processInventoryChange(int slot, const ItemPtr& item)
{
if(item)
item->setPos(Position(65535, slot, 0));
item->setPosition(Position(65535, slot, 0));
g_lua.callGlobalField("Game","onInventoryChange", slot, item);
}
@ -202,7 +202,7 @@ void Game::processInventoryChange(int slot, const ItemPtr& item)
void Game::processCreatureMove(const CreaturePtr& creature, const Position& oldPos, const Position& newPos)
{
// animate walk
if(oldPos.isInRange(newPos, 1, 1, 0))
if(oldPos.isInRange(newPos, 1, 1))
creature->walk(oldPos, newPos);
}
@ -237,16 +237,8 @@ void Game::walk(Otc::Direction direction)
if(!m_localPlayer->canWalk(direction))
return;
// TODO: restore check for blockable tiles
/*
if(toTile && !toTile->isWalkable() && !fromTile->getElevation() >= 3) {
g_game.processTextMessage("statusSmall", "Sorry, not possible.");
return false;
}*/
// only do prewalk to walkable tiles
TilePtr toTile = g_map.getTile(m_localPlayer->getPos() + Position::getPosFromDirection(direction));
// only do prewalks to walkable tiles
TilePtr toTile = g_map.getTile(m_localPlayer->getPosition().translatedToDirection(direction));
if(toTile && toTile->isWalkable())
m_localPlayer->preWalk(direction);
else
@ -316,7 +308,7 @@ void Game::look(const ThingPtr& thing)
int stackpos = getThingStackpos(thing);
if(stackpos != -1)
m_protocolGame->sendLookAt(thing->getPos(), thing->getId(), stackpos);
m_protocolGame->sendLookAt(thing->getPosition(), thing->getId(), stackpos);
}
void Game::open(const ThingPtr& thing, int containerId)
@ -326,7 +318,7 @@ void Game::open(const ThingPtr& thing, int containerId)
int stackpos = getThingStackpos(thing);
if(stackpos != -1)
m_protocolGame->sendUseItem(thing->getPos(), thing->getId(), stackpos, containerId);
m_protocolGame->sendUseItem(thing->getPosition(), thing->getId(), stackpos, containerId);
}
void Game::use(const ThingPtr& thing)
@ -338,7 +330,7 @@ void Game::use(const ThingPtr& thing)
int stackpos = getThingStackpos(thing);
if(stackpos != -1)
m_protocolGame->sendUseItem(thing->getPos(), thing->getId(), stackpos, 0);
m_protocolGame->sendUseItem(thing->getPosition(), thing->getId(), stackpos, 0);
}
void Game::useWith(const ThingPtr& fromThing, const ThingPtr& toThing)
@ -346,7 +338,7 @@ void Game::useWith(const ThingPtr& fromThing, const ThingPtr& toThing)
if(!isOnline() || !fromThing || !toThing || !checkBotProtection())
return;
Position pos = fromThing->getPos();
Position pos = fromThing->getPosition();
int fromStackpos = getThingStackpos(fromThing);
if(fromStackpos == -1)
return;
@ -360,7 +352,7 @@ void Game::useWith(const ThingPtr& fromThing, const ThingPtr& toThing)
if(toStackpos == -1)
return;
m_protocolGame->sendUseItemEx(pos, fromThing->getId(), fromStackpos, toThing->getPos(), toThing->getId(), toStackpos);
m_protocolGame->sendUseItemEx(pos, fromThing->getId(), fromStackpos, toThing->getPosition(), toThing->getId(), toStackpos);
}
}
@ -379,13 +371,13 @@ void Game::useInventoryItem(int itemId, const ThingPtr& toThing)
if(CreaturePtr creature = toThing->asCreature()) {
m_protocolGame->sendUseOnCreature(pos, itemId, 0, creature->getId());
} else {
m_protocolGame->sendUseItemEx(pos, itemId, 0, toThing->getPos(), toThing->getId(), toStackpos);
m_protocolGame->sendUseItemEx(pos, itemId, 0, toThing->getPosition(), toThing->getId(), toStackpos);
}
}
void Game::move(const ThingPtr& thing, const Position& toPos, int count)
{
if(!isOnline() || !thing || !checkBotProtection() || thing->getPos() == toPos || count <= 0)
if(!isOnline() || !thing || !checkBotProtection() || thing->getPosition() == toPos || count <= 0)
return;
m_localPlayer->lockWalk();
@ -394,7 +386,7 @@ void Game::move(const ThingPtr& thing, const Position& toPos, int count)
if(stackpos == -1)
return;
m_protocolGame->sendThrow(thing->getPos(), thing->getId(), stackpos, toPos, count);
m_protocolGame->sendThrow(thing->getPosition(), thing->getId(), stackpos, toPos, count);
}
void Game::attack(const CreaturePtr& creature)
@ -433,6 +425,9 @@ void Game::follow(const CreaturePtr& creature)
void Game::cancelFollow()
{
if(!isOnline() || !checkBotProtection())
return;
m_localPlayer->setFollowingCreature(nullptr);
m_protocolGame->sendFollow(0);
}
@ -444,16 +439,15 @@ void Game::rotate(const ThingPtr& thing)
int stackpos = getThingStackpos(thing);
if(stackpos != -1)
m_protocolGame->sendRotateItem(thing->getPos(), thing->getId(), stackpos);
m_protocolGame->sendRotateItem(thing->getPosition(), thing->getId(), stackpos);
}
//TODO: move this to Thing class
int Game::getThingStackpos(const ThingPtr& thing)
{
// thing is at map
if(thing->getPos().x != 65535) {
TilePtr tile = g_map.getTile(thing->getPos());
if(tile)
if(thing->getPosition().x != 65535) {
if(TilePtr tile = g_map.getTile(thing->getPosition()))
return tile->getThingStackpos(thing);
else {
logError("could not get tile");
@ -467,21 +461,28 @@ int Game::getThingStackpos(const ThingPtr& thing)
void Game::talk(const std::string& message)
{
talkChannel("say", 0, message);
talkChannel(Otc::SpeakSay, 0, message);
}
void Game::talkChannel(const std::string& speakTypeDesc, int channelId, const std::string& message)
void Game::talkChannel(Otc::SpeakType speakType, int channelId, const std::string& message)
{
if(!isOnline() || !checkBotProtection())
return;
m_protocolGame->sendTalk(speakTypeDesc, channelId, "", message);
m_protocolGame->sendTalk(speakType, channelId, "", message);
}
void Game::talkPrivate(const std::string& speakTypeDesc, const std::string& receiver, const std::string& message)
void Game::talkPrivate(Otc::SpeakType speakType, const std::string& receiver, const std::string& message)
{
if(!isOnline() || !checkBotProtection())
return;
m_protocolGame->sendTalk(speakTypeDesc, 0, receiver, message);
m_protocolGame->sendTalk(speakType, 0, receiver, message);
}
void Game::openPrivateChannel(const std::string& receiver)
{
if(!isOnline() || !checkBotProtection())
return;
m_protocolGame->sendOpenPrivateChannel(receiver);
}
void Game::requestChannels()

View File

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

View File

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

View File

@ -33,22 +33,23 @@ public:
static ItemPtr create(int id);
enum {
TICKS_PER_FRAME = 500
};
void draw(const Point& dest, float scaleFactor, bool animate);
void draw(const Point& p, const Rect&);
void setId(uint32 id);
void setCountOrSubType(uint8 value) { m_countOrSubType = value; }
void setCount(int count) { setCountOrSubType(count); }
void setSubType(int subType) { setCountOrSubType(subType); }
void setPos(const Position &position);
void setData(int data);
int getData() { return m_data; }
ThingType *getType();
uint8 getCountOrSubType() { return m_countOrSubType; }
int getSubType();
int getCount();
uint32 getId() { return m_id; }
ItemPtr asItem() { return std::static_pointer_cast<Item>(shared_from_this()); }
private:
int m_data;
uint16 m_id;
uint8 m_countOrSubType;
};
#endif

View File

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

View File

@ -28,254 +28,122 @@
#include "missile.h"
#include "statictext.h"
#include <framework/graphics/graphics.h>
#include <framework/graphics/framebuffer.h>
#include <framework/graphics/paintershaderprogram.h>
#include <framework/graphics/paintershadersources.h>
#include <framework/graphics/texture.h>
#include <framework/core/eventdispatcher.h>
#include "mapview.h"
#include <framework/core/resourcemanager.h>
Map g_map;
Map::Map()
void Map::addMapView(const MapViewPtr& mapView)
{
setVisibleSize(Size(MAP_VISIBLE_WIDTH, MAP_VISIBLE_HEIGHT));
m_mapViews.push_back(mapView);
}
void Map::draw(const Rect& rect)
void Map::removeMapView(const MapViewPtr& mapView)
{
if(!m_framebuffer) {
Size fboSize(m_visibleSize.width() * NUM_TILE_PIXELS, m_visibleSize.height() * NUM_TILE_PIXELS);
m_framebuffer = FrameBufferPtr(new FrameBuffer(fboSize));
m_framebuffer->setClearColor(Fw::black);
auto it = std::find(m_mapViews.begin(), m_mapViews.end(), mapView);
if(it != m_mapViews.end())
m_mapViews.erase(it);
}
void Map::notificateTileUpdateToMapViews(const Position& pos)
{
for(const MapViewPtr& mapView : m_mapViews)
mapView->onTileUpdate(pos);
}
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());
}
g_painter.setColor(Fw::white);
m_framebuffer->bind();
// draw offsets
LocalPlayerPtr localPlayer = g_game.getLocalPlayer();
if(localPlayer)
m_drawOffset = localPlayer->getWalkOffset();
//TODO: cache first/last visible floor
// draw from bottom floors to top floors
int firstFloor = getFirstVisibleFloor();
const int lastFloor = MAX_Z-1;
for(int iz = lastFloor; iz >= firstFloor; --iz) {
// draw tiles like linus pauling's rule order
const int numDiagonals = m_size.width() + m_size.height() - 1;
for(int diagonal = 0; diagonal < numDiagonals; ++diagonal) {
// loop through / diagonal tiles
for(int ix = std::min(diagonal, m_size.width() - 1), iy = std::max(diagonal - m_size.width(), 0); ix >= 0 && iy < m_size.height(); --ix, ++iy) {
// position on current floor
Position tilePos(m_centralPosition.x + (ix - m_centralOffset.x), m_centralPosition.y + (iy - m_centralOffset.y), m_centralPosition.z);
// adjust tilePos to the wanted floor
tilePos.perspectiveUp(m_centralPosition.z - iz);
//TODO: cache visible tiles, m_tiles[] has a high cost (50% fps decrease)
if(const TilePtr& tile = m_tiles[tilePos]) {
// skip tiles that are behind another tile
//if(isCompletlyCovered(tilePos, firstFloor))
// continue;
tile->draw(positionTo2D(tilePos) - m_drawOffset, rect);
void Map::load()
{
if(!g_resources.fileExists("/map.otcmap"))
return;
std::stringstream in;
g_resources.loadFile("/map.otcmap", in);
while(!in.eof()) {
Position pos;
in.read((char*)&pos, sizeof(pos));
uint16 id;
in.read((char*)&id, sizeof(id));
while(id != 0xFFFF) {
ItemPtr item = Item::create(id);
if(item->isStackable() || item->isFluidContainer() || item->isFluid()) {
uint8 countOrSubType;
in.read((char*)&countOrSubType, sizeof(countOrSubType));
item->setCountOrSubType(countOrSubType);
}
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);
}
}
void Map::save()
{
std::stringstream out;
m_framebuffer->release();
g_painter.setCustomProgram(m_shaderProgram);
g_painter.setColor(Fw::white);
m_framebuffer->draw(rect);
g_painter.releaseCustomProgram();
// calculate stretch factor
float horizontalStretchFactor = rect.width() / (float)(m_visibleSize.width() * NUM_TILE_PIXELS);
float verticalStretchFactor = rect.height() / (float)(m_visibleSize.height() * NUM_TILE_PIXELS);
// draw player names and health bars
//TODO: this must be cached with creature walks
for(int x = 0; x < m_visibleSize.width(); ++x) {
for(int y = 0; y < m_visibleSize.height(); ++y) {
Position tilePos = Position(m_centralPosition.x + (x - m_centralOffset.x + 1), m_centralPosition.y + (y - m_centralOffset.y + 1), m_centralPosition.z);
if(const TilePtr& tile = m_tiles[tilePos]) {
auto creatures = tile->getCreatures();
if(creatures.size() == 0)
for(auto& pair : m_tiles) {
Position pos = pair.first;
TilePtr tile = pair.second;
if(!tile || tile->isEmpty())
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;
out.write((char*)&pos, sizeof(pos));
uint16 id;
for(const ThingPtr& thing : tile->getThings()) {
if(ItemPtr item = thing->asItem()) {
id = item->getId();
out.write((char*)&id, sizeof(id));
if(item->isStackable() || item->isFluidContainer() || item->isFluid()) {
uint8 countOrSubType = item->getCountOrSubType();
out.write((char*)&countOrSubType, sizeof(countOrSubType));
}
}
}
id = 0xFFFF;
out.write((char*)&id, sizeof(id));
}
creature->drawInformation(rect.x() + p.x*horizontalStretchFactor, rect.y() + p.y*verticalStretchFactor, isCovered(tilePos, firstFloor), rect);
}
}
}
}
// draw static text
for(auto it = m_staticTexts.begin(), end = m_staticTexts.end(); it != end; ++it) {
Point pos = positionTo2D((*it)->getPos()) - m_drawOffset;
pos.x *= horizontalStretchFactor;
pos.y *= verticalStretchFactor;
(*it)->draw(rect.topLeft() + pos, rect);
}
// draw animated text
for(auto it = m_animatedTexts.begin(), end = m_animatedTexts.end(); it != end; ++it) {
Point pos = positionTo2D((*it)->getPos()) - m_drawOffset;
pos.x *= horizontalStretchFactor;
pos.y *= verticalStretchFactor;
(*it)->draw(rect.topLeft() + pos, rect);
}
g_resources.saveFile("/map.otcmap", out);
}
void Map::clean()
{
m_tiles.clear();
m_creatures.clear();
for(int i=0;i<MAX_Z-1;++i)
m_missilesAtFloor[i].clear();
m_knownCreatures.clear();
for(int i=0;i<=Otc::MAX_Z;++i)
m_floorMissiles[i].clear();
m_animatedTexts.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)
{
if(!thing)
return;
Position oldPos = thing->getPos();
bool teleport = false;
if(oldPos.isValid() && !oldPos.isInRange(pos,1,1,0))
teleport = true;
TilePtr tile = getTile(pos);
TilePtr tile = getOrCreateTile(pos);
if(CreaturePtr creature = thing->asCreature()) {
Position oldPos = thing->getPosition();
tile->addThing(thing, stackPos);
m_creatures[creature->getId()] = creature;
if(teleport)
if(oldPos.isValid() && !oldPos.isInRange(pos,1,1))
g_game.processCreatureTeleport(creature);
}
else if(MissilePtr shot = thing->asMissile()) {
m_missilesAtFloor[shot->getPos().z].push_back(shot);
}
else if(AnimatedTextPtr animatedText = thing->asAnimatedText()) {
} else if(MissilePtr missile = thing->asMissile()) {
m_floorMissiles[pos.z].push_back(missile);
} else if(AnimatedTextPtr animatedText = thing->asAnimatedText()) {
m_animatedTexts.push_back(animatedText);
}
else if(StaticTextPtr staticText = thing->asStaticText()) {
} else if(StaticTextPtr staticText = thing->asStaticText()) {
bool mustAdd = true;
for(auto it = m_staticTexts.begin(), end = m_staticTexts.end(); it != end; ++it) {
StaticTextPtr cStaticText = *it;
if(cStaticText->getPos() == pos) {
if(cStaticText->getPosition() == pos) {
// try to combine messages
if(cStaticText->addMessage(staticText->getName(), staticText->getMessageType(), staticText->getFirstMessage())) {
mustAdd = false;
break;
}
else {
} else {
// must add another message and rearrenge current
}
}
@ -284,113 +152,249 @@ void Map::addThing(const ThingPtr& thing, const Position& pos, int stackPos)
if(mustAdd)
m_staticTexts.push_back(staticText);
}
else {
} else {
tile->addThing(thing, stackPos);
}
thing->start();
thing->setPos(pos);
thing->startAnimation();
thing->setPosition(pos);
notificateTileUpdateToMapViews(pos);
}
ThingPtr Map::getThing(const Position& pos, int stackPos)
{
if(const TilePtr& tile = m_tiles[pos])
if(TilePtr tile = getTile(pos))
return tile->getThing(stackPos);
return nullptr;
}
void Map::removeThingByPos(const Position& pos, int stackPos)
{
if(TilePtr& tile = m_tiles[pos])
tile->removeThingByStackpos(stackPos);
}
void Map::removeThing(const ThingPtr& thing)
bool Map::removeThing(const ThingPtr& thing)
{
if(!thing)
return;
return false;
if(MissilePtr shot = thing->asMissile()) {
auto it = std::find(m_missilesAtFloor[shot->getPos().z].begin(), m_missilesAtFloor[shot->getPos().z].end(), shot);
if(it != m_missilesAtFloor[shot->getPos().z].end()) {
m_missilesAtFloor[shot->getPos().z].erase(it);
if(MissilePtr missile = thing->asMissile()) {
auto it = std::find(m_floorMissiles[missile->getPosition().z].begin(), m_floorMissiles[missile->getPosition().z].end(), missile);
if(it != m_floorMissiles[missile->getPosition().z].end()) {
m_floorMissiles[missile->getPosition().z].erase(it);
return true;
}
return;
}
else if(AnimatedTextPtr animatedText = thing->asAnimatedText()) {
} else if(AnimatedTextPtr animatedText = thing->asAnimatedText()) {
auto it = std::find(m_animatedTexts.begin(), m_animatedTexts.end(), animatedText);
if(it != m_animatedTexts.end())
if(it != m_animatedTexts.end()) {
m_animatedTexts.erase(it);
return;
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);
if(it != m_staticTexts.end())
if(it != m_staticTexts.end()) {
m_staticTexts.erase(it);
return;
return true;
}
} else if(TilePtr tile = thing->getTile())
return tile->removeThing(thing);
if(TilePtr& tile = m_tiles[thing->getPos()])
tile->removeThing(thing);
notificateTileUpdateToMapViews(thing->getPosition());
return false;
}
TilePtr Map::getTile(const Position& pos)
bool Map::removeThingByPos(const Position& pos, int stackPos)
{
if(!pos.isValid())
return nullptr;
if(TilePtr tile = getTile(pos))
return removeThing(tile->getThing(stackPos));
return false;
}
TilePtr& tile = m_tiles[pos];
if(!tile)
tile = TilePtr(new Tile(pos));
TilePtr Map::createTile(const Position& pos)
{
TilePtr tile = TilePtr(new Tile(pos));
m_tiles[pos] = tile;
return tile;
}
const TilePtr& Map::getTile(const Position& pos)
{
auto it = m_tiles.find(pos);
if(it != m_tiles.end())
return it->second;
static TilePtr nulltile;
return nulltile;
}
TilePtr Map::getOrCreateTile(const Position& pos)
{
const TilePtr& tile = getTile(pos);
if(tile)
return tile;
else
return createTile(pos);
}
void Map::cleanTile(const Position& pos)
{
if(TilePtr& tile = m_tiles[pos])
if(TilePtr tile = getTile(pos)) {
tile->clean();
if(tile->canErase())
m_tiles.erase(m_tiles.find(pos));
notificateTileUpdateToMapViews(pos);
}
}
void Map::addCreature(const CreaturePtr& creature)
{
m_creatures[creature->getId()] = creature;
m_knownCreatures[creature->getId()] = creature;
}
CreaturePtr Map::getCreatureById(uint32 id)
{
if(g_game.getLocalPlayer() && (uint32)g_game.getLocalPlayer()->getId() == id)
return g_game.getLocalPlayer();
return m_creatures[id];
LocalPlayerPtr localPlayer = g_game.getLocalPlayer();
if(localPlayer && localPlayer->getId() == id)
return localPlayer;
return m_knownCreatures[id];
}
void Map::removeCreatureById(uint32 id)
{
m_creatures.erase(id);
if(id == 0)
return;
m_knownCreatures.erase(id);
}
void Map::setCentralPosition(const Position& centralPosition)
{
bool teleported = !m_centralPosition.isInRange(centralPosition, 1,1);
m_centralPosition = centralPosition;
}
void Map::setVisibleSize(const Size& visibleSize)
{
m_visibleSize = visibleSize;
if(m_visibleSize.width() > MAX_WIDTH || m_visibleSize.height() > MAX_HEIGHT)
m_visibleSize = Size(MAP_VISIBLE_WIDTH, MAP_VISIBLE_HEIGHT);
m_centralOffset = Point(std::ceil(m_visibleSize.width() / 2.0), std::ceil(m_visibleSize.height() / 2.0));
m_size = m_visibleSize + Size(3, 3);
if(m_framebuffer) {
m_framebuffer->resize(Size(m_visibleSize.width() * NUM_TILE_PIXELS, m_visibleSize.height() * NUM_TILE_PIXELS));
// 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);
}
}
}
}
Point Map::positionTo2D(const Position& position)
std::vector<CreaturePtr> Map::getSpectators(const Position& centerPos, bool multiFloor)
{
return Point((m_centralOffset.x - 1 + (position.x - m_centralPosition.x) - (m_centralPosition.z - position.z)) * NUM_TILE_PIXELS,
(m_centralOffset.y - 1 + (position.y - m_centralPosition.y) - (m_centralPosition.z - position.z)) * NUM_TILE_PIXELS);
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;
}
bool Map::isLookPossible(const Position& pos)
{
TilePtr tile = getTile(pos);
return tile && tile->isLookPossible();
}
bool Map::isCovered(const Position& pos, int firstFloor)
{
// check for tiles on top of the postion
Position tilePos = pos;
while(tilePos.coveredUp() && tilePos.z >= firstFloor) {
TilePtr tile = getTile(tilePos);
// the below tile is covered when the above tile has a full ground
if(tile && tile->isFullGround())
return true;
}
return false;
}
bool Map::isCompletelyCovered(const Position& pos, int firstFloor)
{
Position tilePos = pos;
while(tilePos.coveredUp() && tilePos.z >= firstFloor) {
bool covered = true;
// check in 2x2 range tiles that has no transparent pixels
for(int x=0;x<2;++x) {
for(int y=0;y<2;++y) {
const TilePtr& tile = getTile(tilePos.translated(-x, -y));
if(!tile || !tile->isFullyOpaque()) {
covered = false;
break;
}
}
}
if(covered)
return true;
}
return false;
}
bool Map::isAwareOfPosition(const Position& pos)
{
if(pos.z < getFirstAwareFloor() || pos.z > getLastAwareFloor())
return false;
Position groundedPos = pos;
while(groundedPos.z != m_centralPosition.z) {
if(groundedPos.z > m_centralPosition.z)
groundedPos.coveredUp();
else
groundedPos.coveredDown();
}
return m_centralPosition.isInRange(groundedPos, Otc::AWARE_X_LEFT_TILES,
Otc::AWARE_X_RIGHT_TILES,
Otc::AWARE_Y_TOP_TILES,
Otc::AWARE_Y_BOTTOM_TILES);
}
int Map::getFirstAwareFloor()
{
if(m_centralPosition.z > Otc::SEA_FLOOR)
return m_centralPosition.z-Otc::AWARE_UNDEGROUND_FLOOR_RANGE;
else
return 0;
}
int Map::getLastAwareFloor()
{
if(m_centralPosition.z > Otc::SEA_FLOOR)
return std::min(m_centralPosition.z+Otc::AWARE_UNDEGROUND_FLOOR_RANGE, (int)Otc::MAX_Z);
else
return Otc::SEA_FLOOR;
}

View File

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

View File

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

118
src/otclient/core/mapview.h Normal file
View File

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

View File

@ -27,73 +27,62 @@
#include <framework/core/clock.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&)
{
float time = (g_clock.ticks() - m_startTicks) / m_duration;
internalDraw(p + Point(m_posDelta.x * time, m_posDelta.y * time), 0);
int xPattern = 0, yPattern = 0;
if(m_direction == Otc::NorthWest) {
xPattern = 0;
yPattern = 0;
} else if(m_direction == Otc::North) {
xPattern = 1;
yPattern = 0;
} else if(m_direction == Otc::NorthEast) {
xPattern = 2;
yPattern = 0;
} else if(m_direction == Otc::East) {
xPattern = 2;
yPattern = 1;
} else if(m_direction == Otc::SouthEast) {
xPattern = 2;
yPattern = 2;
} else if(m_direction == Otc::South) {
xPattern = 1;
yPattern = 2;
} else if(m_direction == Otc::SouthWest) {
xPattern = 0;
yPattern = 2;
} else if(m_direction == Otc::West) {
xPattern = 0;
yPattern = 1;
} else {
xPattern = 1;
yPattern = 1;
}
float fraction = m_animationTimer.ticksElapsed() / m_duration;
internalDraw(dest + m_delta * fraction * scaleFactor, scaleFactor, xPattern, yPattern, 0, 0);
}
void Missile::setPath(const Position& fromPosition, const Position& toPosition)
{
Otc::Direction direction = fromPosition.getDirectionFromPosition(toPosition);
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_posDelta = toPosition - fromPosition;
m_startTicks = g_clock.ticks();
m_duration = 150 * std::sqrt(Point(m_posDelta.x, m_posDelta.y).length());
m_posDelta.x *= Map::NUM_TILE_PIXELS;
m_posDelta.y *= Map::NUM_TILE_PIXELS;
m_position = fromPosition;
m_delta = Point(toPosition.x - fromPosition.x, toPosition.y - fromPosition.y);
m_duration = 150 * std::sqrt(m_delta.length());
m_delta *= Otc::TILE_PIXELS;
m_animationTimer.restart();
// schedule removal
auto self = asMissile();
g_dispatcher.scheduleEvent([self]() {
g_map.removeThing(self);
}, m_duration);
g_dispatcher.scheduleEvent([self]() { g_map.removeThing(self); }, m_duration);
}
ThingType *Missile::getType()
void Missile::setId(uint32 id)
{
return g_thingsType.getThingType(m_id, ThingsType::Missile);
m_id = id;
m_type = g_thingsType.getThingType(m_id, ThingsType::Missile);
}

View File

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

View File

@ -22,6 +22,7 @@
#include "spritemanager.h"
#include <framework/core/resourcemanager.h>
#include <framework/core/eventdispatcher.h>
#include <framework/graphics/graphics.h>
SpriteManager g_sprites;
@ -54,6 +55,21 @@ void SpriteManager::unload()
m_signature = 0;
}
void SpriteManager::preloadSprites()
{
// preload every 100 sprites, periodically
const int burst = 50;
const int interval = 10;
auto preload = [this](int start) {
for(int i=start;i<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)
{
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);
uchar pixels[4096];
static std::vector<uint8> pixels(4096);
int writePos = 0;
int read = 0;
@ -116,7 +132,10 @@ TexturePtr SpriteManager::loadSpriteTexture(int id)
writePos += 4;
}
return TexturePtr(new Texture(32, 32, 4, pixels));
TexturePtr spriteTex(new Texture(32, 32, 4, &pixels[0]));
spriteTex->setSmooth(true);
spriteTex->generateBilinearMipmaps(pixels);
return spriteTex;
}
TexturePtr SpriteManager::getSpriteTexture(int id)

View File

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

View File

@ -31,17 +31,22 @@ StaticText::StaticText()
m_font = g_fonts.getFont("verdana-11px-rounded");
}
void StaticText::draw(const Point& p, const Rect& visibleRect)
void StaticText::draw(const Point& dest, const Rect& parentRect)
{
Rect rect = Rect(p - Point(m_textSize.width() / 2, m_textSize.height()) + Point(20, 5), m_textSize);
Rect rect = Rect(dest - Point(m_textSize.width() / 2, m_textSize.height()) + Point(20, 5), m_textSize);
Rect boundRect = rect;
boundRect.bind(visibleRect);
if((boundRect.center() - rect.center()).length() < visibleRect.width() / 15)
boundRect.bind(parentRect);
// draw only if the real center is not too far from the parent center, or its a yell
if((boundRect.center() - rect.center()).length() < parentRect.width() / 15 || isYell()) {
//TODO: cache into a framebuffer
m_font->renderText(m_text, boundRect, Fw::AlignCenter, m_color);
}
}
bool StaticText::addMessage(const std::string& name, const std::string& type, const std::string& message)
bool StaticText::addMessage(const std::string& name, Otc::SpeakType type, const std::string& message)
{
//TODO: this could be moved to lua
// First message
if(m_messages.size() == 0) {
m_name = name;
@ -59,7 +64,7 @@ bool StaticText::addMessage(const std::string& name, const std::string& type, co
auto self = asStaticText();
g_dispatcher.scheduleEvent([self]() {
self->removeMessage();
}, std::max<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;
}
@ -72,40 +77,34 @@ void StaticText::removeMessage()
// schedule removal
auto self = asStaticText();
g_dispatcher.addEvent([self]() { g_map.removeThing(self); });
}
else
} else
compose();
}
void StaticText::compose()
{
//TODO: this could be moved to lua
std::string text;
text.clear();
if(m_messageType == "say") {
if(m_messageType == Otc::SpeakSay) {
text += m_name;
text += " says:\n";
m_color = Color(239, 239, 0);
}
else if(m_messageType == "whisper") {
} else if(m_messageType == Otc::SpeakWhisper) {
text += m_name;
text += " whispers:\n";
m_color = Color(239, 239, 0);
}
else if(m_messageType == "yell") {
} else if(m_messageType == Otc::SpeakYell) {
text += m_name;
text += " yells:\n";
m_color = Color(239, 239, 0);
}
else if(m_messageType == "monsterSay" || m_messageType == "monsterYell") {
} else if(m_messageType == Otc::SpeakMonsterSay || m_messageType == Otc::SpeakMonsterYell) {
m_color = Color(254, 101, 0);
}
else if(m_messageType == "npcToPlayer") {
} else if(m_messageType == Otc::SpeakPrivateNpcToPlayer) {
text += m_name;
text += " says:\n";
m_color = Color(95, 247, 247);
}
else {
} else {
logWarning("unknown speak type: ", m_messageType);
}
@ -116,6 +115,6 @@ void StaticText::compose()
text += "\n";
}
m_text = m_font->wrapText(text, 200);
m_text = m_font->wrapText(text, Otc::MAX_STATIC_TEXT_WIDTH);
m_textSize = m_font->calculateTextRectSize(m_text);
}

View File

@ -29,20 +29,17 @@
class StaticText : public Thing
{
public:
enum {
DURATION_PER_CHARACTER = 75,
MIN_DURATION = 3000
};
StaticText();
void draw(const Point& p, const Rect& visibleRect);
void draw(const Point& dest, const Rect& parentRect);
std::string getName() { return m_name; }
std::string getMessageType() { return m_messageType; }
Otc::SpeakType getMessageType() { return m_messageType; }
std::string getFirstMessage() { return m_messages[0]; }
bool addMessage(const std::string& name, const std::string& type, const std::string& message);
bool isYell() { return m_messageType == Otc::SpeakYell || m_messageType == Otc::SpeakMonsterYell; }
bool addMessage(const std::string& name, Otc::SpeakType type, const std::string& message);
void removeMessage();
StaticTextPtr asStaticText() { return std::static_pointer_cast<StaticText>(shared_from_this()); }
@ -52,9 +49,10 @@ private:
FontPtr m_font;
Size m_textSize;
Boolean<false> m_yell;
std::vector<std::string> m_messages;
std::string m_name, m_text;
std::string m_messageType;
Otc::SpeakType m_messageType;
Color m_color;
};

View File

@ -24,59 +24,59 @@
#include "spritemanager.h"
#include "thingstype.h"
#include <framework/graphics/graphics.h>
#include "map.h"
#include "tile.h"
Thing::Thing()
{
m_id = 0;
m_xPattern = 0;
m_yPattern = 0;
m_zPattern = 0;
m_animation = 0;
m_type = getType();
}
void Thing::internalDraw(const Point& p, int layer)
{
for(int h = 0; h < m_type->dimensions[ThingType::Height]; h++) {
for(int w = 0; w < m_type->dimensions[ThingType::Width]; w++) {
int spriteId = m_type->getSpriteId(w, h, layer, m_xPattern, m_yPattern, m_zPattern, m_animation);
if(!spriteId)
continue;
TexturePtr spriteTex = g_sprites.getSpriteTexture(spriteId);
Rect drawRect((p.x - w*32) - m_type->parameters[ThingType::DisplacementX],
(p.y - h*32) - m_type->parameters[ThingType::DisplacementY],
32, 32);
g_painter.drawTexturedRect(drawRect, spriteTex);
}
}
}
void Thing::setId(uint32 id)
{
m_id = id;
m_type = getType();
m_type = g_thingsType.getEmptyThingType();
}
int Thing::getStackPriority()
{
if(m_type->properties[ThingType::IsGround])
if(isGround())
return 0;
else if(m_type->properties[ThingType::IsGroundBorder])
else if(isGroundBorder())
return 1;
else if(m_type->properties[ThingType::IsOnBottom])
else if(isOnBottom())
return 2;
else if(m_type->properties[ThingType::IsOnTop])
else if(isOnTop())
return 3;
else if(asCreature())
return 4;
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);
}

View File

@ -24,7 +24,7 @@
#define THING_H
#include "declarations.h"
#include "thingtype.h"
#include "thingstype.h"
#include <framework/luascript/luaobject.h>
struct Light
@ -39,23 +39,17 @@ public:
Thing();
virtual ~Thing() { }
virtual void start() {}
virtual void startAnimation() { }
virtual void draw(const Point& dest, float scaleFactor, bool animate) { }
virtual void draw(const Point& p, const Rect&) = 0;
virtual void setId(uint32 id) { }
void setPosition(const Position& position) { m_position = position; }
void setId(uint32 id);
virtual void setPos(const Position& position) { m_pos = position; }
uint32 getId() const { return m_id; }
Position getPos() const { return m_pos; }
virtual uint32 getId() { return 0; }
Position getPosition() { return m_position; }
int getStackPriority();
virtual ThingType *getType();
int getAnimationPhases() { return m_type->dimensions[ThingType::AnimationPhases]; }
int getGroundSpeed() { return m_type->parameters[ThingType::GroundSpeed]; }
void setXPattern(int xPattern) { m_xPattern = xPattern; }
void setYPattern(int yPattern) { m_yPattern = yPattern; }
void setZPattern(int zPattern) { m_zPattern = zPattern; }
const TilePtr& getTile();
int getStackpos();
ThingPtr asThing() { return std::static_pointer_cast<Thing>(shared_from_this()); }
virtual ItemPtr asItem() { return nullptr; }
@ -69,29 +63,55 @@ public:
virtual AnimatedTextPtr asAnimatedText() { return nullptr; }
virtual StaticTextPtr asStaticText() { return nullptr; }
// type related
bool isGround() { return m_type->properties[ThingType::IsGround]; }
bool isFullGround() { return m_type->properties[ThingType::IsFullGround]; }
bool isTranslucent() { return m_type->properties[ThingType::IsTranslucent]; }
bool isGroundBorder() { return m_type->properties[ThingType::IsGroundBorder]; }
bool isOnBottom() { return m_type->properties[ThingType::IsOnBottom]; }
bool isOnTop() { return m_type->properties[ThingType::IsOnTop]; }
bool isDontHide() { return m_type->properties[ThingType::DontHide]; }
bool isContainer() { return m_type->properties[ThingType::IsContainer]; }
bool isForceUse() { return m_type->properties[ThingType::IsForceUse]; }
bool isMultiUse() { return m_type->properties[ThingType::IsMultiUse]; }
bool isRotateable() { return m_type->properties[ThingType::IsRotateable]; }
bool isNotMoveable() { return m_type->properties[ThingType::IsNotMovable]; }
bool isNotWalkable() { return m_type->properties[ThingType::NotWalkable]; }
bool isPickupable() { return m_type->properties[ThingType::IsPickupable]; }
bool ignoreLook() { return m_type->properties[ThingType::IgnoreLook]; }
bool isIgnoreLook() { return m_type->properties[ThingType::IgnoreLook]; }
bool isHangable() { return m_type->properties[ThingType::IsHangable]; }
bool isHookSouth() { return m_type->properties[ThingType::HookSouth]; }
bool isHookEast() { return m_type->properties[ThingType::HookEast]; }
bool isStackable() { return m_type->properties[ThingType::IsStackable]; }
bool isAnimateAlways() { return m_type->properties[ThingType::AnimateAlways]; }
bool isLyingCorpse() { return m_type->properties[ThingType::IsLyingCorpse]; }
bool blocksProjectile() { return m_type->properties[ThingType::BlockProjectile]; }
bool isFluid() { return m_type->properties[ThingType::IsFluid]; }
bool isFluidContainer() { return m_type->properties[ThingType::IsFluidContainer]; }
Size getDimension() { return Size(m_type->dimensions[ThingType::Width], m_type->dimensions[ThingType::Height]); }
int getDimensionWidth() { return m_type->dimensions[ThingType::Width]; }
int getDimensionHeight() { return m_type->dimensions[ThingType::Height]; }
Point getDisplacement() { return Point(m_type->parameters[ThingType::DisplacementX], m_type->parameters[ThingType::DisplacementY]); }
int getNumPatternsX() { return m_type->dimensions[ThingType::PatternX]; }
int getNumPatternsY() { return m_type->dimensions[ThingType::PatternY]; }
int getNumPatternsZ() { return m_type->dimensions[ThingType::PatternZ]; }
int getDisplacementX() { return m_type->parameters[ThingType::DisplacementX]; }
int getDisplacementY() { return m_type->parameters[ThingType::DisplacementY]; }
int getLayers() { return m_type->dimensions[ThingType::Layers]; }
int getAnimationPhases() { return m_type->dimensions[ThingType::AnimationPhases]; }
int getGroundSpeed() { return m_type->parameters[ThingType::GroundSpeed]; }
int getElevation() { return m_type->parameters[ThingType::Elevation]; }
int getSpriteId(int w = 0, int h = 0, int layer = 0,
int xPattern = 0, int yPattern = 0, int zPattern = 0,
int animation = 0) { return m_type->getSpriteId(w, h, layer, xPattern, yPattern, zPattern, animation); }
protected:
void internalDraw(const Point& p, int layer);
void internalDraw(const Point& dest, float scaleFactor, int w, int h, int xPattern, int yPattern, int zPattern, int layer, int animationPhase);
void internalDraw(const Point& dest, float scaleFactor, int xPattern, int yPattern, int zPattern, int animationPhase);
uint32 m_id;
Position m_pos;
Position m_position;
ThingType *m_type;
int m_xPattern, m_yPattern, m_zPattern, m_animation;
};
#endif

View File

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

View File

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

View File

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

View File

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

View File

@ -58,12 +58,12 @@ void OTClient::registerLuaFunctions()
g_lua.bindClassStaticFunction("g_sprites", "load", std::bind(&SpriteManager::load, &g_sprites, _1));
g_lua.bindClassStaticFunction("g_sprites", "isLoaded", std::bind(&SpriteManager::isLoaded, &g_sprites));
g_lua.bindClassStaticFunction("g_sprites", "getSignature", std::bind(&SpriteManager::getSignature, &g_sprites));
g_lua.bindClassStaticFunction("g_sprites", "preloadSprites", std::bind(&SpriteManager::preloadSprites, &g_sprites));
g_lua.registerStaticClass("g_map");
g_lua.bindClassStaticFunction("g_map", "getFirstVisibleFloor", std::bind(&Map::getFirstVisibleFloor, &g_map));
g_lua.bindClassStaticFunction("g_map", "isLookPossible", std::bind(&Map::isLookPossible, &g_map, _1));
g_lua.bindClassStaticFunction("g_map", "isCovered", std::bind(&Map::isCovered, &g_map, _1, _2));
g_lua.bindClassStaticFunction("g_map", "isCompletlyCovered", std::bind(&Map::isCompletlyCovered, &g_map, _1, _2));
g_lua.bindClassStaticFunction("g_map", "isCompletelyCovered", std::bind(&Map::isCompletelyCovered, &g_map, _1, _2));
g_lua.bindClassStaticFunction("g_map", "addThing", std::bind(&Map::addThing, &g_map, _1, _2, _3));
g_lua.bindClassStaticFunction("g_map", "getThing", std::bind(&Map::getThing, &g_map, _1, _2));
g_lua.bindClassStaticFunction("g_map", "removeThingByPos", std::bind(&Map::removeThingByPos, &g_map, _1, _2));
@ -72,13 +72,9 @@ void OTClient::registerLuaFunctions()
g_lua.bindClassStaticFunction("g_map", "getTile", std::bind(&Map::getTile, &g_map, _1));
g_lua.bindClassStaticFunction("g_map", "setCentralPosition", std::bind(&Map::setCentralPosition, &g_map, _1));
g_lua.bindClassStaticFunction("g_map", "getCentralPosition", std::bind(&Map::getCentralPosition, &g_map));
g_lua.bindClassStaticFunction("g_map", "addCreature", std::bind(&Map::addCreature, &g_map, _1));
g_lua.bindClassStaticFunction("g_map", "getCreatureById", std::bind(&Map::getCreatureById, &g_map, _1));
g_lua.bindClassStaticFunction("g_map", "removeCreatureById", std::bind(&Map::removeCreatureById, &g_map, _1));
g_lua.bindClassStaticFunction("g_map", "setVisibleSize", std::bind(&Map::setVisibleSize, &g_map, _1));
g_lua.bindClassStaticFunction("g_map", "getVibibleSize", std::bind(&Map::getVibibleSize, &g_map));
g_lua.bindClassStaticFunction("g_map", "getCentralOffset", std::bind(&Map::getCentralOffset, &g_map));
g_lua.bindClassStaticFunction("g_map", "positionTo2D", std::bind(&Map::positionTo2D, &g_map, _1));
g_lua.bindClassStaticFunction("g_map", "getSpectators", std::bind(&Map::getSpectators, &g_map, _1, _2));
g_lua.bindGlobalFunction("getOufitColor", Outfit::getColor);
@ -91,14 +87,11 @@ void OTClient::registerLuaFunctions()
g_lua.registerClass<Thing>();
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>("getPos", &Thing::getPos);
g_lua.bindClassMemberFunction<Thing>("getPosition", &Thing::getPosition);
g_lua.bindClassMemberFunction<Thing>("getStackPriority", &Thing::getStackPriority);
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>("asItem", &Thing::asItem);
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>("isNotMoveable", &Thing::isNotMoveable);
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>("isHookSouth", &Thing::isHookSouth);
g_lua.bindClassMemberFunction<Thing>("isTranslucent", &Thing::isTranslucent);
g_lua.bindClassMemberFunction<Thing>("isFullGround", &Thing::isFullGround);
g_lua.registerClass<Creature, Thing>();
g_lua.bindClassMemberFunction<Creature>("getName", &Creature::getName);
@ -133,7 +129,7 @@ void OTClient::registerLuaFunctions()
g_lua.registerClass<Item, Thing>();
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<Missile, Thing>();
@ -141,6 +137,7 @@ void OTClient::registerLuaFunctions()
g_lua.registerClass<AnimatedText, Thing>();
g_lua.registerClass<Player, Creature>();
g_lua.bindClassMemberFunction<Creature>("isWalking", &Creature::isWalking);
g_lua.registerClass<Npc, Creature>();
g_lua.registerClass<Monster, Creature>();
g_lua.registerClass<LocalPlayer, Player>();
@ -165,14 +162,13 @@ void OTClient::registerLuaFunctions()
g_lua.bindClassMemberFunction<Tile>("getThing", &Tile::getThing);
g_lua.bindClassMemberFunction<Tile>("getThingStackpos", &Tile::getThingStackpos);
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>("getTopLookThing", &Tile::getTopLookThing);
g_lua.bindClassMemberFunction<Tile>("getTopUseThing", &Tile::getTopUseThing);
g_lua.bindClassMemberFunction<Tile>("getTopCreature", &Tile::getTopCreature);
g_lua.bindClassMemberFunction<Tile>("getTopMoveThing", &Tile::getTopMoveThing);
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>("getCreatures", &Tile::getCreatures);
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>("requestChannels", std::bind(&Game::requestChannels, &g_game));
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>("look", std::bind(&Game::look, &g_game, _1));
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>("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>("turn", std::bind(&Game::turn, &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>("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.registerClass<UIItem, UIWidget>();
g_lua.bindClassStaticFunction<UIItem>("create", []{ return UIItemPtr(new UIItem); } );
g_lua.bindClassMemberFunction<UIItem>("getItem", &UIItem::getItem);
g_lua.bindClassStaticFunction<UIItem>("create", []{ return UIItemPtr(new UIItem); });
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>("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.bindClassStaticFunction<UICreature>("create", []{ return UICreaturePtr(new UICreature); } );
@ -238,6 +245,8 @@ void OTClient::registerLuaFunctions()
g_lua.registerClass<UIMap, UIWidget>();
g_lua.bindClassStaticFunction<UIMap>("create", []{ return UIMapPtr(new UIMap); } );
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.bindClassStaticFunction<UIGame>("create", []{ return UIGamePtr(new UIGame); } );

View File

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

View File

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

View File

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

View File

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

View File

@ -24,6 +24,7 @@
#include <framework/core/modulemanager.h>
#include "core/game.h"
#include <framework/core/resourcemanager.h>
#include "core/map.h"
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.autoLoadModules(1000);
g_map.load();
// load otclientrc.lua
if(g_resources.fileExists("/otclientrc.lua")) {
try {

View File

@ -30,7 +30,7 @@ void UICreature::draw()
if(m_creature) {
g_painter.setColor(Fw::white);
m_creature->draw(m_rect.bottomRight() - Point(32, 32) + Point(m_padding.left, m_padding.top), m_rect);
m_creature->draw(m_rect.bottomRight() - Point(32, 32) + Point(m_padding.left, m_padding.top), 1, false);
}
drawChildren();

View File

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

View File

@ -38,10 +38,10 @@ void UIItem::draw()
Point topLeft = m_rect.bottomRight() - Point(32, 32) + Point(m_padding.left, m_padding.top);
g_painter.setColor(Fw::white);
m_item->draw(topLeft, m_rect);
m_item->draw(topLeft, 1, true);
if(m_font && m_item->isStackable() && m_item->getData() > 1) {
std::string count = Fw::tostring(m_item->getData());
if(m_font && m_item->isStackable() && m_item->getCount() > 1) {
std::string count = Fw::tostring(m_item->getCount());
m_font->renderText(count, Rect(m_rect.topLeft(), m_rect.bottomRight() - Point(3, 0)), Fw::AlignBottomRight, Color(231, 231, 231));
}
@ -51,3 +51,30 @@ void UIItem::draw()
drawChildren();
}
void UIItem::setItemId(int id)
{
if(!m_item)
m_item = Item::create(id);
else {
// remove item
if(id == 0)
m_item = nullptr;
else
m_item->setId(id);
}
}
void UIItem::onStyleApply(const std::string& styleName, const OTMLNodePtr& styleNode)
{
UIWidget::onStyleApply(styleName, styleNode);
for(const OTMLNodePtr& node : styleNode->children()) {
if(node->tag() == "item-id")
setItemId(node->value<int>());
else if(node->tag() == "item-count")
setItemCount(node->value<int>());
else if(node->tag() == "virtual")
setVirtual(node->value<bool>());
}
}

View File

@ -33,12 +33,24 @@ public:
UIItem();
void draw();
void setItemId(int id);
void setItemCount(int count) { if(m_item) m_item->setCount(count); }
void setItemSubType(int subType) { if(m_item) m_item->setSubType(subType); }
void setItem(const ItemPtr& item) { m_item = item; }
void setVirtual(bool virt) { m_virtual = virt; }
void clearItem() { setItemId(0); }
int getItemId() { return m_item ? m_item->getId() : 0; }
int getItemCount() { return m_item ? m_item->getCount() : 0; }
int getItemSubType() { return m_item ? m_item->getSubType() : 0; }
ItemPtr getItem() { return m_item; }
bool isVirtual() { return m_virtual; }
protected:
void onStyleApply(const std::string& styleName, const OTMLNodePtr& styleNode);
ItemPtr m_item;
Boolean<false> m_virtual;
};
#endif

View File

@ -23,6 +23,7 @@
#include "uimap.h"
#include <otclient/core/game.h>
#include <otclient/core/map.h>
#include <otclient/core/mapview.h>
#include <framework/otml/otml.h>
#include <framework/graphics/graphics.h>
#include <otclient/core/localplayer.h>
@ -30,21 +31,79 @@
UIMap::UIMap()
{
m_dragable = true;
m_mapView = MapViewPtr(new MapView);
g_map.addMapView(m_mapView);
m_mapView->followCreature(g_game.getLocalPlayer());
}
UIMap::~UIMap()
{
g_map.removeMapView(m_mapView);
}
void UIMap::draw()
{
drawSelf();
// draw map border
g_painter.setColor(Fw::black);
g_painter.drawBoundingRect(m_mapRect.expanded(1));
g_map.draw(m_mapRect);
m_mapView->draw(m_mapRect);
drawChildren();
}
void UIMap::zoomIn()
{
int dimensionHeight = m_mapView->getVisibleDimension().height() * 0.99;
if(dimensionHeight == m_mapView->getVisibleDimension().height())
dimensionHeight -= 1;
if(dimensionHeight % 2 == 0)
dimensionHeight -= 1;
int dimensionWidth = dimensionHeight * getSize().ratio();
if(dimensionWidth % 2 == 0)
dimensionWidth -= 1;
m_mapView->setVisibleDimension(Size(dimensionWidth, dimensionHeight));
Rect mapRect = getChildrenRect().expanded(-1);
Size mapSize = m_mapView->getVisibleSize();
mapSize.scale(mapRect.size(), Fw::KeepAspectRatio);
m_mapRect.resize(mapSize);
m_mapRect.moveCenter(m_rect.center());
}
void UIMap::zoomOut()
{
int dimensionHeight = m_mapView->getVisibleDimension().height() * 1.01;
if(dimensionHeight == m_mapView->getVisibleDimension().height())
dimensionHeight += 1;
if(dimensionHeight % 2 == 0)
dimensionHeight += 1;
int dimensionWidth = dimensionHeight * getSize().ratio();
if(dimensionWidth % 2 == 0)
dimensionWidth += 1;
m_mapView->setVisibleDimension(Size(dimensionWidth, dimensionHeight));
Rect mapRect = getChildrenRect().expanded(-1);
Size mapSize = m_mapView->getVisibleSize();
mapSize.scale(mapRect.size(), Fw::KeepAspectRatio);
m_mapRect.resize(mapSize);
m_mapRect.moveCenter(m_rect.center());
}
void UIMap::setCameraPosition(const Position& pos)
{
m_mapView->setCameraPosition(pos);
}
TilePtr UIMap::getTile(const Point& mousePos)
{
/*
if(!m_mapRect.contains(mousePos))
return nullptr;
@ -55,12 +114,12 @@ TilePtr UIMap::getTile(const Point& mousePos)
if(localPlayer)
relativeStretchMousePos += localPlayer->getWalkOffset();
Size mapSize(g_map.getVibibleSize().width() * Map::NUM_TILE_PIXELS, g_map.getVibibleSize().height() * Map::NUM_TILE_PIXELS);
Size mapSize(g_map.getVibibleSize().width() * Otc::TILE_PIXELS, g_map.getVibibleSize().height() * Otc::TILE_PIXELS);
PointF stretchFactor(m_mapRect.width() / (float)mapSize.width(), m_mapRect.height() / (float)mapSize.height());
PointF relativeMousePos = PointF(relativeStretchMousePos.x, relativeStretchMousePos.y) / stretchFactor;
PointF tilePosF = relativeMousePos / Map::NUM_TILE_PIXELS;
PointF tilePosF = relativeMousePos / Otc::TILE_PIXELS;
Position tilePos = Position(1 + (int)tilePosF.x - g_map.getCentralOffset().x, 1 + (int)tilePosF.y - g_map.getCentralOffset().y, 0) + g_map.getCentralPosition();
if(!tilePos.isValid())
return nullptr;
@ -70,7 +129,7 @@ TilePtr UIMap::getTile(const Point& mousePos)
// We must check every floor, from top to bottom to check for a clickable tile
int firstFloor = g_map.getFirstVisibleFloor();
tilePos.perspectiveUp(tilePos.z - firstFloor);
tilePos.coveredUp(tilePos.z - firstFloor);
for(int i = firstFloor; i <= Map::MAX_Z; i++) {
tile = g_map.getTile(tilePos);
if(tile && tile->isClickable())
@ -84,12 +143,14 @@ TilePtr UIMap::getTile(const Point& mousePos)
return nullptr;
return tile;
*/
return nullptr;
}
void UIMap::onGeometryChange(const Rect& oldRect, const Rect& newRect)
{
Rect mapRect = getChildrenRect().expanded(-1);
Size mapSize(g_map.getVibibleSize().width() * Map::NUM_TILE_PIXELS, g_map.getVibibleSize().height() * Map::NUM_TILE_PIXELS);
Size mapSize = m_mapView->getVisibleSize();
mapSize.scale(mapRect.size(), Fw::KeepAspectRatio);
m_mapRect.resize(mapSize);

View File

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

View File

@ -30,51 +30,99 @@
class Position
{
public:
Position() : x(-1), y(-1), z(-1) { }
Position(int x, int y, int z) : x(x), y(y), z(z) { }
Position() : x(65535), y(65535), z(255) { }
Position(uint16 x, uint16 y, uint8 z) : x(x), y(y), z(z) { }
static Position getPosFromDirection(Otc::Direction direction) {
Position translatedToDirection(Otc::Direction direction) {
Position pos = *this;
switch(direction) {
case Otc::North:
return Position( 0, -1, 0);
pos.y--;
break;
case Otc::East:
return Position( 1, 0, 0);
pos.x++;
break;
case Otc::South:
return Position( 0, 1, 0);
pos.y++;
break;
case Otc::West:
return Position(-1, 0, 0);
pos.x--;
break;
case Otc::NorthEast:
return Position( 1, -1, 0);
pos.x++;
pos.y--;
break;
case Otc::SouthEast:
return Position( 1, 1, 0);
pos.x++;
pos.y++;
break;
case Otc::SouthWest:
return Position(-1, 1, 0);
pos.x--;
pos.y++;
break;
case Otc::NorthWest:
return Position(-1, -1, 0);
default:
return Position();
pos.x--;
pos.y--;
break;
}
return pos;
}
Position translatedToReverseDirection(Otc::Direction direction) {
Position pos = *this;
switch(direction) {
case Otc::North:
pos.y++;
break;
case Otc::East:
pos.x--;
break;
case Otc::South:
pos.y--;
break;
case Otc::West:
pos.x++;
break;
case Otc::NorthEast:
pos.x--;
pos.y++;
break;
case Otc::SouthEast:
pos.x--;
pos.y--;
break;
case Otc::SouthWest:
pos.x++;
pos.y--;
break;
case Otc::NorthWest:
pos.x++;
pos.y++;
break;
}
return pos;
}
Otc::Direction getDirectionFromPosition(const Position& position) const {
Position positionDelta = position - *this;
int dx = position.x - x;
int dy = position.y - y;
if(positionDelta.x == 0 && positionDelta.y == 0)
if(dx == 0 && dy == 0)
return Otc::InvalidDirection;
else if(positionDelta.x == 0) {
if(positionDelta.y < 0)
else if(dx == 0) {
if(dy < 0)
return Otc::North;
else if(positionDelta.y > 0)
else if(dy > 0)
return Otc::South;
}
else if(positionDelta.y == 0) {
if(positionDelta.x < 0)
else if(dy == 0) {
if(dx < 0)
return Otc::West;
else if(positionDelta.x > 0)
else if(dx > 0)
return Otc::East;
}
else {
float angle = std::atan2(positionDelta.y * -1, positionDelta.x) * RAD_TO_DEC;
float angle = std::atan2(dy * -1, dx) * RAD_TO_DEC;
if(angle < 0)
angle += 360;
@ -98,7 +146,10 @@ public:
return Otc::InvalidDirection;
}
bool isValid() const { return x >= 0 && y >= 0 && z >= 0 && x <= 65535 && y <= 65535 && z <= 255; }
bool isValid() const { return !(x == 65535 && y == 65535 && z == 255); }
void translate(int dx, int dy, short dz = 0) { x += dx; y += dy; z += dz; }
Position translated(int dx, int dy, short dz = 0) const { Position pos = *this; pos.x += dx; pos.y += dy; pos.z += dz; return pos; }
Position operator+(const Position& other) const { return Position(x + other.x, y + other.y, z + other.z); }
Position& operator+=(const Position& other) { x+=other.x; y+=other.y; z +=other.z; return *this; }
@ -109,19 +160,50 @@ public:
bool operator==(const Position& other) const { return other.x == x && other.y == y && other.z == z; }
bool operator!=(const Position& other) const { return other.x!=x || other.y!=y || other.z!=z; }
bool isInRange(const Position& pos, int xdif, int ydif, int zdif = 1) const {
return std::abs(x-pos.x) <= xdif && std::abs(y-pos.y) <= ydif && std::abs(pos.z-z) <= zdif;
bool isInRange(const Position& pos, int xRange, int yRange) const { return std::abs(x-pos.x) <= xRange && std::abs(y-pos.y) <= yRange && z == pos.z; }
bool isInRange(const Position& pos, int minXRange, int maxXRange, int minYRange, int maxYRange) const {
return (pos.x >= x-minXRange && pos.x <= x+maxXRange && pos.y >= y-minYRange && pos.y <= y+maxYRange && pos.z == z);
}
void up(int n = 1) { z-=n; }
void down(int n = 1) { z+=n; }
bool up(int n = 1) {
int nz = z-n;
if(nz >= 0 && nz <= Otc::MAX_Z) {
z = nz;
return true;
}
return false;
}
void perspectiveUp(int n = 1) { x+=n; y+=n; z-=n; }
void coveredDown(int n = 1) { x-=n; y-=n; z+=n; }
bool down(int n = 1) {
int nz = z+n;
if(nz >= 0 && nz <= Otc::MAX_Z) {
z = nz;
return true;
}
return false;
}
int x;
int y;
int z;
bool coveredUp(int n = 1) {
int nx = x+n, ny = y+n, nz = z-n;
if(nx >= 0 && nx <= 65535 && ny >= 0 && ny <= 65535 && nz >= 0 && nz <= Otc::MAX_Z) {
x = nx; y = ny; z = nz;
return true;
}
return false;
}
bool coveredDown(int n = 1) {
int nx = x-n, ny = y-n, nz = z+n;
if(nx >= 0 && nx <= 65535 && ny >= 0 && ny <= 65535 && nz >= 0 && nz <= Otc::MAX_Z) {
x = nx; y = ny; z = nz;
return true;
}
return false;
}
uint16 x;
uint16 y;
uint8 z;
};
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)
{
out << pos.x << " " << pos.y << " " << pos.z;
out << (int)pos.x << " " << (int)pos.y << " " << (int)pos.z;
return out;
}
inline std::istream& operator>>(std::istream& in, Position& pos)
{
in >> pos.x >> pos.y >> pos.z;
int x, y, z;
in >> x >> y >> z;
pos.x = x;
pos.y = y;
pos.z = z;
return in;
}