rework map rendering

This commit is contained in:
Eduardo Bart 2012-01-29 22:00:12 -02:00
parent 4276bd680d
commit 9db7bd2602
62 changed files with 1244 additions and 752 deletions

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

@ -36,10 +36,10 @@ function UIMap:onDrop(widget, mousePos)
spinbox:setCurrentIndex(data)
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

@ -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;
@ -88,6 +100,11 @@ 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,10 +24,12 @@
#define FRAMEBUFFER_H
#include "declarations.h"
#include "texture.h"
class FrameBuffer
{
public:
FrameBuffer();
FrameBuffer(const Size& size);
virtual ~FrameBuffer();
@ -36,10 +38,12 @@ public:
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

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

@ -128,6 +128,70 @@ std::vector<uint8> Texture::getPixels()
return pixels;
}
void Texture::generateBilinearMipmaps()
{
std::vector<uint8> inPixels = getPixels();
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 = outPixels;
inSize /= 2;
outSize /= 2;
}
}
void Texture::setupFilters()
{
GLint minFilter;

View File

@ -35,6 +35,7 @@ public:
void bind() { glBindTexture(GL_TEXTURE_2D, m_textureId); }
void generateMipmaps();
void generateBilinearMipmaps();
void setSmooth(bool smooth);
GLuint getId() { return m_textureId; }

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);
@ -409,7 +409,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

@ -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,10 +52,14 @@ 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; }

View File

@ -33,7 +33,7 @@ public:
BUFFER_MAXSIZE = 16384,
HEADER_POS = 0,
HEADER_LENGTH = 2,
CHECKSUM_POS = 2,
CHECKSUm_position = 2,
CHECKSUM_LENGTH = 4,
DATA_POS = 6,
UNENCRYPTED_DATA_POS = 8

View File

@ -36,7 +36,7 @@ public:
BUFFER_MAXSIZE = 1024,
HEADER_POS = 0,
HEADER_LENGTH = 2,
CHECKSUM_POS = 2,
CHECKSUm_position = 2,
CHECKSUM_LENGTH = 4,
DATA_POS = 6
};

View File

@ -71,7 +71,7 @@ void Protocol::send(OutputMessage& outputMessage)
// set checksum
uint32 checksum = getAdlerChecksum(outputMessage.getBuffer() + OutputMessage::DATA_POS, outputMessage.getMessageSize());
outputMessage.setWritePos(OutputMessage::CHECKSUM_POS);
outputMessage.setWritePos(OutputMessage::CHECKSUm_position);
outputMessage.addU32(checksum);
// set size
@ -107,7 +107,7 @@ void Protocol::internalRecvHeader(uint8* buffer, uint16 size)
void Protocol::internalRecvData(uint8* buffer, uint16 size)
{
memcpy(m_inputMessage.getBuffer() + InputMessage::CHECKSUM_POS, buffer, size);
memcpy(m_inputMessage.getBuffer() + InputMessage::CHECKSUm_position, buffer, size);
if(m_checksumEnabled) {
uint32 checksum = getAdlerChecksum(m_inputMessage.getBuffer() + InputMessage::DATA_POS, m_inputMessage.getMessageSize() - InputMessage::CHECKSUM_LENGTH);

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

@ -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,36 @@ namespace Otc
static const char* AppCompactName = "otclient";
static const char* AppVersion = "0.4.0";
enum {
TILE_PIXELS = 32,
SEA_LEVEL = 7,
MAX_Z = 15,
VISIBLE_X_TILES = 15,
VISIBLE_Y_TILES = 11,
MAX_ELEVATION = 24,
EFFECT_TICKS_PER_FRAME = 75,
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 MapDrawFlags {
MapDrawNonMoveableItems = 1,
MapDrawMoveableItems = 2,
MapDrawCreatures = 4,
MapDrawCreaturesInformation = 8,
MapDrawStaticTexts = 16,
MapDrawAnimatedTexts = 32,
MapDrawEffects = 64,
MapDrawMissiles = 128,
MapDrawEverything = MapDrawNonMoveableItems | MapDrawMoveableItems |
MapDrawCreatures | MapDrawCreaturesInformation |
MapDrawStaticTexts | MapDrawAnimatedTexts |
MapDrawEffects | MapDrawMissiles
};
enum DatOpts {
DatGround = 0,
DatGroundClip,

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

@ -63,16 +63,19 @@ 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)
{
int scaledTileSize = Otc::TILE_PIXELS * scaleFactor;
Point scaledWalkOffset = m_walkOffset * scaleFactor;
if(m_showVolatileSquare) {
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 + scaledWalkOffset - (getDisplacement() + 3)*scaleFactor, Size(28*scaleFactor, 28*scaleFactor)), 2*scaleFactor);
}
if(m_showStaticSquare) {
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 + scaledWalkOffset - (getDisplacement() + 1)*scaleFactor, Size(scaledTileSize, scaledTileSize)), 2*scaleFactor);
}
g_painter.setColor(Fw::white);
@ -90,7 +93,7 @@ void Creature::draw(const Point& p, const Rect&)
// Render creature
if(m_outfit.getCategory() == ThingsType::Creature) {
for(m_yPattern = 0; m_yPattern < m_type->dimensions[ThingType::PatternY]; m_yPattern++) {
for(m_yPattern = 0; m_yPattern < getNumPatternsY(); m_yPattern++) {
// continue if we dont have this addon.
if(m_yPattern > 0 && !(m_outfit.getAddons() & (1 << (m_yPattern-1))))
@ -104,24 +107,24 @@ 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);
for(int h = 0; h < getDimensionHeight(); h++) {
for(int w = 0; w < getDimensionWidth(); w++) {
int spriteId = 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);
if(getLayers() > 1) {
int maskId = 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);
}
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);
Rect drawRect((dest.x + scaledWalkOffset.x - w*scaledTileSize) - getDisplacementX()*scaleFactor,
(dest.y + scaledWalkOffset.y - h*scaledTileSize) - getDisplacementY()*scaleFactor,
scaledTileSize, scaledTileSize);
g_painter.drawTexturedRect(drawRect, spriteTex);
}
}
@ -130,14 +133,14 @@ void Creature::draw(const Point& p, const Rect&)
}
}
else if(m_outfit.getCategory() == ThingsType::Item) {
for(int l = 0; l < m_type->dimensions[ThingType::Layers]; l++)
internalDraw(p + m_walkOffset, l);
for(int l = 0; l < getLayers(); l++)
internalDraw(dest + scaledWalkOffset, scaleFactor, l);
}
else if(m_outfit.getCategory() == ThingsType::Effect)
internalDraw(p + m_walkOffset, 0);
internalDraw(dest + scaledWalkOffset, scaleFactor, 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 +148,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 +176,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 +212,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;
@ -248,10 +256,10 @@ void Creature::updateWalkAnimation(int totalPixelsWalked)
if(m_outfit.getCategory() != ThingsType::Creature)
return;
if(totalPixelsWalked == 32 || totalPixelsWalked == 0 || m_type->dimensions[ThingType::AnimationPhases] <= 1)
if(totalPixelsWalked == 32 || totalPixelsWalked == 0 || getAnimationPhases() <= 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);
else if(getAnimationPhases() > 1)
m_animation = 1 + ((totalPixelsWalked * 4) / Otc::TILE_PIXELS) % (getAnimationPhases() - 1);
}
void Creature::updateWalkOffset(int totalPixelsWalked)
@ -375,7 +383,7 @@ void Creature::setDirection(Otc::Direction direction)
void Creature::setOutfit(const Outfit& outfit)
{
m_outfit = outfit;
m_type = getType();
updateType();
m_animation = 0;
if(m_outfit.getCategory() == ThingsType::Effect) {
@ -389,7 +397,7 @@ void Creature::setOutfit(const Outfit& outfit)
m_yPattern = 0;
}
if(m_outfit.getCategory() == ThingsType::Creature && m_type->dimensions[ThingType::Layers] == 1) {
if(m_outfit.getCategory() == ThingsType::Creature && getLayers() == 1) {
m_outfit.resetClothes();
}
}
@ -483,8 +491,5 @@ void Creature::updateShield()
m_showShieldTexture = true;
}
ThingType *Creature::getType()
{
return g_thingsType.getThingType(m_outfit.getId(), m_outfit.getCategory());
}

View File

@ -41,8 +41,8 @@ public:
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);
void drawInformation(const Point& point, bool useGray, const Rect& parentRect);
void setName(const std::string& name);
void setHealthPercent(uint8 healthPercent);

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

View File

@ -24,28 +24,21 @@
#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);
Effect();
void draw(const Point& p, const Rect&);
void start();
void startAnimation();
void updateAnimation();
ThingType *getType();
EffectPtr asEffect() { return std::static_pointer_cast<Effect>(shared_from_this()); }
private:
ticks_t m_animationStartTicks;
Timer m_animationTimer;
};
#endif

View File

@ -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);
}
@ -246,7 +246,7 @@ void Game::walk(Otc::Direction direction)
}*/
// only do prewalk to walkable tiles
TilePtr toTile = g_map.getTile(m_localPlayer->getPos() + Position::getPosFromDirection(direction));
TilePtr toTile = g_map.getTile(m_localPlayer->getPosition() + Position::getPositionFromDirection(direction));
if(toTile && toTile->isWalkable())
m_localPlayer->preWalk(direction);
else
@ -316,7 +316,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 +326,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 +338,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 +346,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 +360,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 +379,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 +394,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 +433,9 @@ void Game::follow(const CreaturePtr& creature)
void Game::cancelFollow()
{
if(!isOnline() || !checkBotProtection())
return;
m_localPlayer->setFollowingCreature(nullptr);
m_protocolGame->sendFollow(0);
}
@ -444,16 +447,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");

View File

@ -26,6 +26,9 @@
#include "thing.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()
{
@ -39,29 +42,40 @@ ItemPtr Item::create(int id)
return item;
}
void Item::draw(const Point& p, const Rect&)
{
if(m_type->dimensions[ThingType::AnimationPhases] > 1)
m_animation = (g_clock.ticks() % (TICKS_PER_FRAME * m_type->dimensions[ThingType::AnimationPhases])) / TICKS_PER_FRAME;
PainterShaderProgramPtr itemProgram;
for(int l = 0; l < m_type->dimensions[ThingType::Layers]; l++)
internalDraw(p, l);
void Item::draw(const Point& dest, float scaleFactor)
{
if(getAnimationPhases() > 1)
m_animation = (g_clock.ticks() % (Otc::ITEM_TICKS_PER_FRAME * getAnimationPhases())) / Otc::ITEM_TICKS_PER_FRAME;
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);
for(int layer = 0; layer < getLayers(); layer++)
internalDraw(dest, scaleFactor, layer);
g_painter.releaseCustomProgram();
}
void Item::setPos(const Position& position)
void Item::setPosition(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];
if(isGround()) {
m_xPattern = position.x % getNumPatternsX();
m_yPattern = position.y % getNumPatternsY();
m_zPattern = position.z % getNumPatternsZ();
}
Thing::setPos(position);
Thing::setPosition(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(isStackable() && getNumPatternsX() == 4 && getNumPatternsY() == 2) {
if(data < 5) {
m_xPattern = data-1;
m_yPattern = 0;
@ -83,15 +97,15 @@ void Item::setData(int data)
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(isHangable()) {
if(isHookSouth()) {
m_xPattern = getNumPatternsX() >= 2 ? 1 : 0;
}
else if(m_type->properties[ThingType::HookEast]) {
m_xPattern = m_type->dimensions[ThingType::PatternX] >= 3 ? 2 : 0;
else if(isHookEast()) {
m_xPattern = getNumPatternsX() >= 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) {
case Otc::FluidNone:
@ -153,14 +167,9 @@ void Item::setData(int data)
break;
}
m_xPattern = (color % 4) % m_type->dimensions[ThingType::PatternX];
m_yPattern = (color / 4) % m_type->dimensions[ThingType::PatternY];
m_xPattern = (color % 4) % getNumPatternsX();
m_yPattern = (color / 4) % getNumPatternsY();
}
m_data = data;
}
ThingType *Item::getType()
{
return g_thingsType.getThingType(m_id, ThingsType::Item);
}

View File

@ -33,17 +33,12 @@ public:
static ItemPtr create(int id);
enum {
TICKS_PER_FRAME = 500
};
void draw(const Point& dest, float scaleFactor);
void draw(const Point& p, const Rect&);
void setPos(const Position &position);
void setPosition(const Position &position);
void setData(int data);
int getData() { return m_data; }
ThingType *getType();
ItemPtr asItem() { return std::static_pointer_cast<Item>(shared_from_this()); }

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 + Position::getPositionFromDirection(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)

View File

@ -28,202 +28,232 @@
#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"
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);
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());
auto it = std::find(m_mapViews.begin(), m_mapViews.end(), mapView);
if(it != m_mapViews.end())
m_mapViews.erase(it);
}
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);
}
}
}
// after drawing all tiles, draw shots
for(const MissilePtr& shot : m_missilesAtFloor[iz]) {
Position missilePos = shot->getPos();
shot->draw(positionTo2D(missilePos) - m_drawOffset, rect);
}
}
m_framebuffer->release();
g_painter.setCustomProgram(m_shaderProgram);
g_painter.setColor(Fw::white);
m_framebuffer->draw(rect);
g_painter.releaseCustomProgram();
// calculate stretch factor
float horizontalStretchFactor = rect.width() / (float)(m_visibleSize.width() * NUM_TILE_PIXELS);
float verticalStretchFactor = rect.height() / (float)(m_visibleSize.height() * NUM_TILE_PIXELS);
// draw player names and health bars
//TODO: this must be cached with creature walks
for(int x = 0; x < m_visibleSize.width(); ++x) {
for(int y = 0; y < m_visibleSize.height(); ++y) {
Position tilePos = Position(m_centralPosition.x + (x - m_centralOffset.x + 1), m_centralPosition.y + (y - m_centralOffset.y + 1), m_centralPosition.z);
if(const TilePtr& tile = m_tiles[tilePos]) {
auto creatures = tile->getCreatures();
if(creatures.size() == 0)
continue;
for(const CreaturePtr& creature : creatures) {
Point p((m_centralOffset.x - 1 + (tilePos.x - m_centralPosition.x))*NUM_TILE_PIXELS + 10 - tile->getDrawElevation(),
(m_centralOffset.y - 1 + (tilePos.y - m_centralPosition.y))*NUM_TILE_PIXELS - 10 - tile->getDrawElevation());
if(creature != localPlayer) {
p += creature->getWalkOffset() - m_drawOffset;
}
creature->drawInformation(rect.x() + p.x*horizontalStretchFactor, rect.y() + p.y*verticalStretchFactor, isCovered(tilePos, firstFloor), rect);
}
}
}
}
// 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);
}
void Map::notificateTileUpdateToMapViews(const Position& pos)
{
for(const MapViewPtr& mapView : m_mapViews)
mapView->onTileUpdate(pos);
}
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()
void Map::addThing(const ThingPtr& thing, const Position& pos, int stackPos)
{
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;
if(!thing)
return;
TilePtr tile = getTile(pos);
if(!tile)
tile = createTile(pos);
if(CreaturePtr creature = thing->asCreature()) {
Position oldPos = thing->getPosition();
tile->addThing(thing, stackPos);
// creature teleported
if(oldPos.isValid() && !oldPos.isInRange(pos,1,1,0))
g_game.processCreatureTeleport(creature);
} 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()) {
bool mustAdd = true;
for(auto it = m_staticTexts.begin(), end = m_staticTexts.end(); it != end; ++it) {
StaticTextPtr cStaticText = *it;
if(cStaticText->getPosition() == pos) {
// try to combine messages
if(cStaticText->addMessage(staticText->getName(), staticText->getMessageType(), staticText->getFirstMessage())) {
mustAdd = false;
break;
} else {
// must add another message and rearrenge current
}
}
}
if(mustAdd)
m_staticTexts.push_back(staticText);
} else {
tile->addThing(thing, stackPos);
}
thing->startAnimation();
thing->setPosition(pos);
notificateTileUpdateToMapViews(pos);
}
ThingPtr Map::getThing(const Position& pos, int stackPos)
{
if(TilePtr tile = getTile(pos))
return tile->getThing(stackPos);
return nullptr;
}
bool Map::removeThing(const ThingPtr& thing)
{
if(!thing) {
return false;
} else 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;
}
} else if(AnimatedTextPtr animatedText = thing->asAnimatedText()) {
auto it = std::find(m_animatedTexts.begin(), m_animatedTexts.end(), animatedText);
if(it != m_animatedTexts.end()) {
m_animatedTexts.erase(it);
return true;
}
} else if(StaticTextPtr staticText = thing->asStaticText()) {
auto it = std::find(m_staticTexts.begin(), m_staticTexts.end(), staticText);
if(it != m_staticTexts.end()) {
m_staticTexts.erase(it);
return true;
}
} else if(TilePtr tile = getTile(thing->getPosition()))
return tile->removeThing(thing);
notificateTileUpdateToMapViews(thing->getPosition());
return false;
}
bool Map::removeThingByPos(const Position& pos, int stackPos)
{
if(TilePtr tile = getTile(pos))
return removeThing(tile->getThing(stackPos));
return false;
}
TilePtr Map::createTile(const Position& pos)
{
TilePtr tile = TilePtr(new Tile(pos));
m_tiles[pos] = tile;
return tile;
}
void Map::cleanTile(const Position& pos)
{
if(TilePtr tile = getTile(pos)) {
tile->clean();
notificateTileUpdateToMapViews(pos);
}
}
void Map::addCreature(const CreaturePtr& creature)
{
m_knownCreatures[creature->getId()] = creature;
}
CreaturePtr Map::getCreatureById(uint32 id)
{
LocalPlayerPtr localPlayer = g_game.getLocalPlayer();
if(localPlayer && localPlayer->getId() == id)
return localPlayer;
return m_knownCreatures[id];
}
void Map::removeCreatureById(uint32 id)
{
if(id == 0)
return;
m_knownCreatures.erase(id);
}
std::vector<CreaturePtr> Map::getSpectators(const Position& centerPos, bool multiFloor)
{
return getSpectatorsInRange(centerPos, multiFloor, (Otc::VISIBLE_X_TILES - 1)/2, (Otc::VISIBLE_Y_TILES - 1)/2);
}
std::vector<CreaturePtr> Map::getSpectatorsInRange(const Position& centerPos, bool multiFloor, int xRange, int yRange)
{
return getSpectatorsInRangeEx(centerPos, multiFloor, xRange, xRange, yRange, yRange);
}
std::vector<CreaturePtr> Map::getSpectatorsInRangeEx(const Position& centerPos, bool multiFloor, int minXRange, int maxXRange, int minYRange, int maxYRange)
{
int minZRange = 0;
int maxZRange = 0;
std::vector<CreaturePtr> creatures;
if(multiFloor) {
minZRange = 0;
maxZRange = Otc::MAX_Z;
}
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 + Position(ix,iy,iz));
if(!tile)
continue;
auto tileCreatures = tile->getCreatures();
creatures.insert(creatures.end(), tileCreatures.begin(), tileCreatures.end());
}
}
}
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;
return creatures;
}
bool Map::isLookPossible(const Position& pos)
{
TilePtr tile = m_tiles[pos];
if(tile)
return tile->isLookPossible();
return true;
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;
tilePos.perspectiveUp();
tilePos.coveredUp();
while(tilePos.z >= firstFloor) {
TilePtr tile = m_tiles[tilePos];
TilePtr tile = getTile(tilePos);
// the below tile is covered when the above tile has a full ground
if(tile && tile->isFullGround())
return true;
tilePos.perspectiveUp();
tilePos.coveredUp();
}
return false;
}
bool Map::isCompletlyCovered(const Position& pos, int firstFloor)
bool Map::isCompletelyCovered(const Position& pos, int firstFloor)
{
Position tilePos = pos;
tilePos.perspectiveUp();
tilePos.coveredUp();
while(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) {
TilePtr tile = m_tiles[tilePos + Position(-x, -y, 0)];
@ -235,162 +265,7 @@ bool Map::isCompletlyCovered(const Position& pos, int firstFloor)
}
if(covered)
return true;
tilePos.perspectiveUp();
tilePos.coveredUp();
}
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);
if(CreaturePtr creature = thing->asCreature()) {
tile->addThing(thing, stackPos);
m_creatures[creature->getId()] = creature;
if(teleport)
g_game.processCreatureTeleport(creature);
}
else if(MissilePtr shot = thing->asMissile()) {
m_missilesAtFloor[shot->getPos().z].push_back(shot);
}
else if(AnimatedTextPtr animatedText = thing->asAnimatedText()) {
m_animatedTexts.push_back(animatedText);
}
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) {
// try to combine messages
if(cStaticText->addMessage(staticText->getName(), staticText->getMessageType(), staticText->getFirstMessage())) {
mustAdd = false;
break;
}
else {
// must add another message and rearrenge current
}
}
}
if(mustAdd)
m_staticTexts.push_back(staticText);
}
else {
tile->addThing(thing, stackPos);
}
thing->start();
thing->setPos(pos);
}
ThingPtr Map::getThing(const Position& pos, int stackPos)
{
if(const TilePtr& tile = m_tiles[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)
{
if(!thing)
return;
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);
}
return;
}
else if(AnimatedTextPtr animatedText = thing->asAnimatedText()) {
auto it = std::find(m_animatedTexts.begin(), m_animatedTexts.end(), animatedText);
if(it != m_animatedTexts.end())
m_animatedTexts.erase(it);
return;
}
else if(StaticTextPtr staticText = thing->asStaticText()) {
auto it = std::find(m_staticTexts.begin(), m_staticTexts.end(), staticText);
if(it != m_staticTexts.end())
m_staticTexts.erase(it);
return;
}
if(TilePtr& tile = m_tiles[thing->getPos()])
tile->removeThing(thing);
}
TilePtr Map::getTile(const Position& pos)
{
if(!pos.isValid())
return nullptr;
TilePtr& tile = m_tiles[pos];
if(!tile)
tile = TilePtr(new Tile(pos));
return tile;
}
void Map::cleanTile(const Position& pos)
{
if(TilePtr& tile = m_tiles[pos])
tile->clean();
}
void Map::addCreature(const CreaturePtr& creature)
{
m_creatures[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];
}
void Map::removeCreatureById(uint32 id)
{
m_creatures.erase(id);
}
void Map::setCentralPosition(const Position& centralPosition)
{
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));
}
}
Point Map::positionTo2D(const Position& position)
{
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);
}

View File

@ -26,70 +26,59 @@
#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 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) { return m_tiles[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) { m_centralPosition = 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);
Light getLight() { return m_light; }
Position getCentralPosition() { return m_centralPosition; }
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,321 @@
/*
* 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"
MapView::MapView()
{
int frameBufferSize = std::min(g_graphics.getMaxTextureSize(), (int)DEFAULT_FRAMBUFFER_SIZE);
m_framebuffer = FrameBufferPtr(new FrameBuffer(Size(frameBufferSize, 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
bool updated = updateVisibleTilesCache();
float scaleFactor = m_tileSize/(float)Otc::TILE_PIXELS;
if(updated || m_animated) {
m_framebuffer->bind();
for(const TilePtr& tile : m_cachedVisibleTiles) {
tile->draw(transformPositionTo2D(tile->getPosition()), scaleFactor);
//TODO: restore missiles
}
m_framebuffer->generateMipmaps();
m_framebuffer->release();
}
g_painter.setCustomProgram(m_shaderProgram);
g_painter.setColor(Fw::white);
Point drawOffset(m_tileSize, 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();
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(tileStretchedSize.width() >= 24) {
for(const CreaturePtr& creature : m_cachedFloorVisibleCreatures) {
const TilePtr& tile = creature->getCurrentTile();
Position pos = tile->getPosition();
Point p = transformPositionTo2D(pos) - drawOffset;
p += (creature->getWalkOffset()-tile->getDrawElevation() + Point(8, -8)) * 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;
Point p = transformPositionTo2D(pos) - drawOffset;
p.x = p.x * horizontalStretchFactor;
p.y = p.y * verticalStretchFactor;
p += rect.topLeft();
animatedText->draw(p, rect);
}
}
}
bool MapView::updateVisibleTilesCache()
{
// update only when needed
if(!m_mustUpdateVisibleTilesCache)
return false;
int firstFloor = getFirstVisibleFloor();
int lastFloor = getLastVisibleFloor();
m_cachedFirstVisibleFloor = firstFloor;
m_cachedLastVisibleFloor = lastFloor;
Position cameraPosition = getCameraPosition();
// clear current visible tiles cache
m_cachedVisibleTiles.clear();
// 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; --iz) {
// draw tiles like linus pauling's rule order
const int numDiagonals = m_drawDimension.width() + m_drawDimension.height() - 1;
for(int diagonal = 0; diagonal < numDiagonals; ++diagonal) {
// loop through / diagonal tiles
for(int ix = std::min(diagonal, m_drawDimension.width() - 1), iy = std::max(diagonal - m_drawDimension.width() + 1, 0); ix >= 0 && iy < m_drawDimension.height(); --ix, ++iy) {
// position on current floor
Position tilePos(cameraPosition.x + (ix - m_virtualCenterOffset.x), cameraPosition.y + (iy - m_virtualCenterOffset.y), cameraPosition.z);
// adjust tilePos to the wanted floor
tilePos.coveredUp(cameraPosition.z - iz);
if(TilePtr tile = g_map.getTile(tilePos)) {
// skip tiles that have nothing
if(tile->getThingCount() == 0)
continue;
// skip tiles that are completely behind another tile
if(g_map.isCompletelyCovered(tilePos, firstFloor))
continue;
m_cachedVisibleTiles.push_back(tile);
}
}
}
}
m_cachedFloorVisibleCreatures = g_map.getSpectators(cameraPosition, false);
m_mustUpdateVisibleTilesCache = false;
return true;
}
void MapView::recalculateTileSize()
{
int possiblesTileSizes[] = {32,16,8,4,2,1};
int foundSize = 0;
for(int candidateTileSize : possiblesTileSizes) {
Size candidateFramebufferSize = m_drawDimension * candidateTileSize;
// found a valid size
if(candidateFramebufferSize.width() <= m_framebuffer->getSize().width() && candidateFramebufferSize.height() <= m_framebuffer->getSize().height()) {
foundSize = candidateTileSize;
break;
}
}
assert(foundSize > 0);
m_tileSize = foundSize;
}
void MapView::onTileUpdate(const Position& pos)
{
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("cannot render less than 3x3 tiles");
return;
}
m_visibleDimension = visibleDimension;
m_drawDimension = visibleDimension + Size(3,3);
m_virtualCenterOffset = (m_drawDimension/2 - Size(1,1)).toPoint();
recalculateTileSize();
dump << m_framebuffer->getSize();
dump << visibleDimension * m_tileSize;
requestVisibleTilesCacheUpdate();
}
int MapView::getFirstVisibleFloor()
{
// return forced first visible floor
if(m_lockedFirstVisibleFloor != -1)
return m_lockedFirstVisibleFloor;
// if nothing is limiting the view, the first visible floor is 0
int firstFloor = 0;
Position cameraPosition = getCameraPosition();
// limits to underground floors while under sea level
if(cameraPosition.z > Otc::SEA_LEVEL)
firstFloor = Otc::SEA_LEVEL+1;
// 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.x + ix, cameraPosition.y + iy, cameraPosition.z);
// process tiles that we can look through, e.g. windows, doors
if((ix == 0 && iy == 0) || g_map.isLookPossible(pos)) {
Position upperPos = pos;
Position coveredPos = pos;
coveredPos.coveredUp();
upperPos.up();
while(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;
}
coveredPos.coveredUp();
upperPos.up();
}
}
}
}
return firstFloor;
}
int MapView::getLastVisibleFloor()
{
Position cameraPosition = getCameraPosition();
// view only underground floors when below sea level
if(cameraPosition.z > Otc::SEA_LEVEL)
return Otc::MAX_Z;
else
return Otc::SEA_LEVEL;
}
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);
}

View File

@ -0,0 +1,97 @@
/*
* 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>
class MapView : public LuaObject
{
enum {
DEFAULT_FRAMBUFFER_SIZE = 2048
};
public:
MapView();
void draw(const Rect& rect);
private:
void recalculateTileSize();
bool updateVisibleTilesCache();
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 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;
Boolean<true> m_mustUpdateVisibleTilesCache;
Boolean<true> m_animated;
std::vector<TilePtr> m_cachedVisibleTiles;
std::vector<CreaturePtr> m_cachedFloorVisibleCreatures;
CreaturePtr m_followingCreature;
FrameBufferPtr m_framebuffer;
PainterShaderProgramPtr m_shaderProgram;
};
#endif

View File

@ -35,7 +35,7 @@ Missile::Missile() : Thing()
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);
//internalDraw(p + Point(m_positionDelta.x * time, m_positionDelta.y * time), 0);
}
void Missile::setPath(const Position& fromPosition, const Position& toPosition)
@ -79,18 +79,16 @@ void Missile::setPath(const Position& fromPosition, const Position& toPosition)
m_yPattern = 1;
}
m_pos = fromPosition;
m_posDelta = toPosition - fromPosition;
m_position = fromPosition;
m_positionDelta = 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_duration = 150 * std::sqrt(Point(m_positionDelta.x, m_positionDelta.y).length());
m_positionDelta.x *= Otc::TILE_PIXELS;
m_positionDelta.y *= Otc::TILE_PIXELS;
// 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()

View File

@ -47,7 +47,7 @@ public:
private:
ticks_t m_startTicks;
Position m_posDelta;
Position m_positionDelta;
float m_duration;
};

View File

@ -116,7 +116,10 @@ TexturePtr SpriteManager::loadSpriteTexture(int id)
writePos += 4;
}
return TexturePtr(new Texture(32, 32, 4, pixels));
TexturePtr spriteTex(new Texture(32, 32, 4, pixels));
spriteTex->setSmooth(true);
spriteTex->generateBilinearMipmaps();
return spriteTex;
}
TexturePtr SpriteManager::getSpriteTexture(int id)

View File

@ -31,17 +31,21 @@ 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);
if((boundRect.center() - rect.center()).length() < parentRect.width() / 15) {
//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)
{
//TODO: this could be moved to lua
// First message
if(m_messages.size() == 0) {
m_name = name;
@ -59,7 +63,11 @@ 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));
if(type == "yell" || type == "monsterYell")
m_yell = true;
return true;
}
@ -72,40 +80,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") {
text += m_name;
text += " says:\n";
m_color = Color(239, 239, 0);
}
else if(m_messageType == "whisper") {
} else if(m_messageType == "whisper") {
text += m_name;
text += " whispers:\n";
m_color = Color(239, 239, 0);
}
else if(m_messageType == "yell") {
} else if(m_messageType == "yell") {
text += m_name;
text += " yells:\n";
m_color = Color(239, 239, 0);
}
else if(m_messageType == "monsterSay" || m_messageType == "monsterYell") {
} else if(m_messageType == "monsterSay" || m_messageType == "monsterYell") {
m_color = Color(254, 101, 0);
}
else if(m_messageType == "npcToPlayer") {
} else if(m_messageType == "npcToPlayer") {
text += m_name;
text += " says:\n";
m_color = Color(95, 247, 247);
}
else {
} else {
logWarning("unknown speak type: ", m_messageType);
}
@ -116,6 +118,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,19 +29,16 @@
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; }
std::string getFirstMessage() { return m_messages[0]; }
bool isYell() { return m_yell; }
bool addMessage(const std::string& name, const std::string& type, const std::string& message);
void removeMessage();
@ -52,6 +49,7 @@ 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;

View File

@ -24,6 +24,7 @@
#include "spritemanager.h"
#include "thingstype.h"
#include <framework/graphics/graphics.h>
#include "map.h"
Thing::Thing()
{
@ -32,51 +33,65 @@ Thing::Thing()
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);
}
}
m_type = g_thingsType.getEmptyThingType();
}
void Thing::setId(uint32 id)
{
m_id = id;
m_type = getType();
updateType();
}
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::getCurrentTile()
{
return g_thingsType.getEmptyThingType();
return g_map.getTile(m_position);
}
void Thing::internalDraw(const Point& dest, float scaleFactor, int layer)
{
int scaledSize = Otc::TILE_PIXELS * scaleFactor;
for(int h = 0; h < getDimensionHeight(); h++) {
for(int w = 0; w < getDimensionWidth(); w++) {
int spriteId = getSpriteId(w, h, layer, m_xPattern, m_yPattern, m_zPattern, m_animation);
if(!spriteId)
continue;
TexturePtr spriteTex = g_sprites.getSpriteTexture(spriteId);
Rect drawRect((dest.x - w*scaledSize) - getDisplacementX()*scaleFactor,
(dest.y - h*scaledSize) - getDisplacementY()*scaleFactor,
scaledSize, scaledSize);
g_painter.drawTexturedRect(drawRect, spriteTex);
}
}
}
void Thing::updateType()
{
if(CreaturePtr creature = asCreature())
m_type = g_thingsType.getThingType(creature->getOutfit().getId(), creature->getOutfit().getCategory());
else if(asItem())
m_type = g_thingsType.getThingType(m_id, ThingsType::Item);
else if(asMissile())
m_type = g_thingsType.getThingType(m_id, ThingsType::Missile);
else if(asEffect())
m_type = g_thingsType.getThingType(m_id, ThingsType::Effect);
else
m_type = g_thingsType.getEmptyThingType();
}

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,19 +39,16 @@ public:
Thing();
virtual ~Thing() { }
virtual void start() {}
virtual void startAnimation() { }
virtual void draw(const Point& dest, float scaleFactor) { }
virtual void draw(const Point& p, const Rect&) = 0;
virtual void setId(uint32 id);
virtual 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; }
uint32 getId() { return m_id; }
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]; }
const TilePtr& getCurrentTile();
void setXPattern(int xPattern) { m_xPattern = xPattern; }
void setYPattern(int yPattern) { m_yPattern = yPattern; }
@ -69,29 +66,53 @@ 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 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 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 layer);
void updateType();
uint32 m_id;
Position m_pos;
ThingType *m_type;
Position m_position;
int m_xPattern, m_yPattern, m_zPattern, m_animation;
private:
ThingType *m_type;
};
#endif

View File

@ -33,49 +33,53 @@
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)
{
m_drawElevation = 0;
// first bottom items
for(const ThingPtr& thing : m_things) {
ThingType *type = thing->getType();
if(!type->properties[ThingType::IsGround] && !type->properties[ThingType::IsGroundBorder] && !type->properties[ThingType::IsOnBottom])
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;
thing->draw(dest - m_drawElevation*scaleFactor, scaleFactor);
m_drawElevation += thing->getElevation();
if(m_drawElevation > Otc::MAX_ELEVATION)
m_drawElevation = Otc::MAX_ELEVATION;
}
// now common items
// 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->asCreature() || thing->isOnTop() || thing->isOnBottom() || thing->isGroundBorder() || thing->isGround())
break;
thing->draw(p - m_drawElevation, visibleRect);
m_drawElevation += type->parameters[ThingType::Elevation];
if(m_drawElevation > MAX_DRAW_ELEVATION)
m_drawElevation = MAX_DRAW_ELEVATION;
thing->draw(dest - m_drawElevation*scaleFactor, scaleFactor);
m_drawElevation += thing->getElevation();
if(m_drawElevation > Otc::MAX_ELEVATION)
m_drawElevation = Otc::MAX_ELEVATION;
}
// we can render creatures in 3x3 range
//TODO: this algorithm is slowing down render too much, but it could be cached to improve framerate
//NOTE: looping for 9 tiles is a dirty way to render walking creatures, must change this later
for(int xi = -1; xi <= 1; ++xi) {
for(int yi = -1; yi <= 1; ++yi) {
for(CreaturePtr creature : g_map.getTile(m_pos + Position(xi, yi, 0))->getCreatures()) {
ThingType *type = creature->getType();
Rect creatureRect(p.x + xi*32 + creature->getWalkOffset().x - type->parameters[ThingType::DisplacementX], p.y + yi*32 + creature->getWalkOffset().y - type->parameters[ThingType::DisplacementY], 32, 32);
Rect thisTileRect(p.x, p.y, 32, 32);
const TilePtr& tile = g_map.getTile(m_position + Position(xi, yi, 0));
if(!tile)
continue;
for(const CreaturePtr& creature : tile->getCreatures()) {
int tileSize = Otc::TILE_PIXELS * scaleFactor;
Rect creatureRect(dest.x + xi*tileSize + (creature->getWalkOffset().x - creature->getDisplacementX())*scaleFactor,
dest.y + yi*tileSize + (creature->getWalkOffset().y - creature->getDisplacementY())*scaleFactor,
tileSize, tileSize);
Rect thisTileRect(dest.x, dest.y, tileSize, tileSize);
// 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);
creature->draw(Point(dest.x + xi*tileSize - m_drawElevation*scaleFactor,
dest.y + yi*tileSize - m_drawElevation*scaleFactor), scaleFactor);
}
}
}
@ -83,13 +87,12 @@ void Tile::draw(const Point& p, const Rect& visibleRect)
// effects
for(const EffectPtr& effect : m_effects)
effect->draw(p - m_drawElevation, visibleRect);
effect->draw(dest, scaleFactor);
// top items
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 - m_drawElevation, scaleFactor);
}
}
@ -104,6 +107,8 @@ ThingPtr Tile::addThing(const ThingPtr& thing, int stackPos)
if(!thing)
return nullptr;
thing->setPosition(m_position);
if(EffectPtr effect = thing->asEffect()) {
m_effects.push_back(effect);
return nullptr;
@ -124,9 +129,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(const 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 +186,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 +201,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 +221,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 +247,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 +296,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 +309,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 +318,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 +332,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 +349,11 @@ bool Tile::isClickable()
return false;
}
bool Tile::isEmpty()
{
return m_things.size() == 0;
}
bool Tile::hasCreature()
{
for(const ThingPtr& thing : m_things)
@ -358,7 +362,11 @@ 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;
}

View File

@ -28,21 +28,25 @@
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);
private:
void updateVisibleItemsCache();
public:
void clean();
ThingPtr addThing(const ThingPtr& thing, int stackPos = -1);
bool removeThing(const ThingPtr& thing);
ThingPtr getThing(int stackPos);
int getThingStackpos(const ThingPtr& thing);
ThingPtr getTopThing();
ThingPtr removeThingByStackpos(int stackPos);
ThingPtr removeThing(const ThingPtr& thing);
void addWalkingCreature(const CreaturePtr& creature);
void removeWalkingCreature(const CreaturePtr& creature);
ThingPtr getTopLookThing();
@ -51,7 +55,7 @@ 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();
ItemPtr getGround();
@ -60,16 +64,19 @@ public:
bool isFullGround();
bool isFullyOpaque();
bool isLookPossible();
bool hasCreature();
bool isEmpty();
bool isClickable();
bool isEmpty();
bool hasCreature();
bool limitsFloorsView();
int getThingCount() { return m_things.size() + m_effects.size(); }
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;
Position m_position;
int m_drawElevation;
};

View File

@ -60,10 +60,9 @@ void OTClient::registerLuaFunctions()
g_lua.bindClassStaticFunction("g_sprites", "getSignature", std::bind(&SpriteManager::getSignature, &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 +71,8 @@ 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.bindGlobalFunction("getOufitColor", Outfit::getColor);
@ -91,9 +85,9 @@ 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);
@ -118,7 +112,7 @@ 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.registerClass<Creature, Thing>();
@ -165,14 +159,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);
@ -238,6 +231,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

@ -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)
@ -866,7 +866,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);
@ -1041,7 +1041,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 +1065,8 @@ ThingPtr ProtocolGame::internalGetThing(InputMessage& msg)
creature->setId(id);
creature->setName(name);
g_map.addCreature(creature);
}
uint8 healthPercent = msg.getU8();

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);
}
drawChildren();

View File

@ -38,7 +38,7 @@ 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);
if(m_font && m_item->isStackable() && m_item->getData() > 1) {
std::string count = Fw::tostring(m_item->getData());

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,56 @@
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 black brounding 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()
{
m_mapView->setVisibleDimension(m_mapView->getVisibleDimension() + Size(2,2));
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()
{
m_mapView->setVisibleDimension(m_mapView->getVisibleDimension() - Size(2,2));
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());
}
TilePtr UIMap::getTile(const Point& mousePos)
{
/*
if(!m_mapRect.contains(mousePos))
return nullptr;
@ -55,12 +91,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 +106,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 +120,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,20 @@ class UIMap : public UIWidget
{
public:
UIMap();
~UIMap();
void draw();
void zoomIn();
void zoomOut();
TilePtr getTile(const Point& mousePos);
protected:
virtual void onGeometryChange(const Rect& oldRect, const Rect& newRect);
private:
MapViewPtr m_mapView;
Rect m_mapRect;
};

View File

@ -33,7 +33,7 @@ public:
Position() : x(-1), y(-1), z(-1) { }
Position(int x, int y, int z) : x(x), y(y), z(z) { }
static Position getPosFromDirection(Otc::Direction direction) {
static Position getPositionFromDirection(Otc::Direction direction) {
switch(direction) {
case Otc::North:
return Position( 0, -1, 0);
@ -116,7 +116,7 @@ public:
void up(int n = 1) { z-=n; }
void down(int n = 1) { z+=n; }
void perspectiveUp(int n = 1) { x+=n; y+=n; z-=n; }
void coveredUp(int n = 1) { x+=n; y+=n; z-=n; }
void coveredDown(int n = 1) { x-=n; y-=n; z+=n; }
int x;