rework walk

This commit is contained in:
Eduardo Bart 2012-01-19 02:12:53 -02:00
parent 4491ee8bdd
commit 6ce92a1a64
22 changed files with 478 additions and 228 deletions

3
BUGS
View File

@ -1,7 +1,8 @@
2x2 corpses is drawn above players
animatedtext is displayed in non visible floors
attack while walking cancels the walk
trying to walking in follow doesn't cancel de walk
trying to walking while following doesn't cancel de walk
attacked/followed creatures are not cleared when it goes out of range
if a spell is used while pressing arrows keys the walk is canceled and it doesnt starts walking again
game map text message boxes is not displayed like tibia
name/shields doesnt follow the creature when walking on parcels

1
TODO
View File

@ -61,6 +61,7 @@ rework win32 key input system
[bart] check for recursive anchors and print a error instead of crashing
[bart] make api to enable/disable capture of events to avoid massive event processing
[bart] review style apply system
rework styles rendering order
== Client
make possible to reload modules

View File

@ -29,3 +29,7 @@ function debugContainersItems()
end
end
end
function quit()
exit()
end

View File

@ -16,3 +16,11 @@ function UIWindow:onKeyPress(keyCode, keyboardModifiers, wouldFilter)
end
end
end
function UIWindow:onMousePress(mousePos, mouseButton)
end
function UIWindow:onGeometryChange(oldRect, newRect)
end

View File

@ -71,7 +71,7 @@ end
local function onApplicationClose()
print('close app')
if Game.isOnline() then
Game.logout(false)
Game.logout(true)
else
exit()
end

View File

@ -81,8 +81,7 @@ end
-- hooked events
local function onGameDeath()
TextMessage.displayEventAdvance('You are dead.', 10)
scheduleEvent(function() Game.logout(true) end, 10*1000)
TextMessage.displayEventAdvance('You are dead.', 60)
end
local function onGameTextMessage(msgtype, msg)

View File

@ -26,6 +26,11 @@
#include <framework/global.h>
class Module;
class Event;
class ScheduledEvent;
typedef std::shared_ptr<Module> ModulePtr;
typedef std::shared_ptr<Event> EventPtr;
typedef std::shared_ptr<ScheduledEvent> ScheduledEventPtr;
#endif

View File

@ -37,29 +37,34 @@ void EventDispatcher::flush()
void EventDispatcher::poll()
{
while(!m_scheduledEventList.empty()) {
if(g_clock.ticks() < m_scheduledEventList.top().ticks)
ScheduledEventPtr scheduledEvent = m_scheduledEventList.top();
if(scheduledEvent->reamaningTicks() > 0)
break;
SimpleCallback callback = std::move(m_scheduledEventList.top().callback);
m_scheduledEventList.pop();
callback();
scheduledEvent->execute();
}
while(!m_eventList.empty()) {
m_eventList.front()();
EventPtr event = m_eventList.front();
m_eventList.pop_front();
event->execute();
}
}
void EventDispatcher::scheduleEvent(const SimpleCallback& callback, int delay)
ScheduledEventPtr EventDispatcher::scheduleEvent(const SimpleCallback& callback, int delay)
{
assert(delay >= 0);
m_scheduledEventList.push(ScheduledEvent(g_clock.ticksFor(delay), callback));
ScheduledEventPtr scheduledEvent(new ScheduledEvent(callback, delay));
m_scheduledEventList.push(scheduledEvent);
return scheduledEvent;
}
void EventDispatcher::addEvent(const SimpleCallback& callback, bool pushFront)
EventPtr EventDispatcher::addEvent(const SimpleCallback& callback, bool pushFront)
{
EventPtr event(new Event(callback));
if(pushFront)
m_eventList.push_front(callback);
m_eventList.push_front(event);
else
m_eventList.push_back(callback);
m_eventList.push_back(event);
return event;
}

View File

@ -24,12 +24,49 @@
#define EVENTDISPATCHER_H
#include "declarations.h"
#include "clock.h"
#include <framework/luascript/luaobject.h>
struct ScheduledEvent {
ScheduledEvent(ticks_t ticks, const SimpleCallback& callback) : ticks(ticks), callback(callback) { }
bool operator<(const ScheduledEvent& other) const { return ticks > other.ticks; }
ticks_t ticks;
SimpleCallback callback;
class Event : public LuaObject
{
public:
Event(const SimpleCallback& callback) : m_callback(callback), m_canceled(false), m_executed(false) { }
void execute() {
if(!m_canceled) {
m_callback();
m_executed = true;
}
}
void cancel() { m_canceled = true; }
bool isCanceled() { return m_canceled; }
bool isExecuted() { return m_executed; }
protected:
SimpleCallback m_callback;
bool m_canceled;
bool m_executed;
};
class ScheduledEvent : public Event
{
public:
ScheduledEvent(const SimpleCallback& callback, int delay) : Event(callback) {
m_ticks = g_clock.ticksFor(delay);
}
int ticks() const { return m_ticks; }
int reamaningTicks() const { return m_ticks - g_clock.ticks(); }
private:
ticks_t m_ticks;
};
struct lessScheduledEvent : std::binary_function<ScheduledEventPtr, ScheduledEventPtr&, bool> {
bool operator()(const ScheduledEventPtr& a, const ScheduledEventPtr& b) const {
return b->ticks() < a->ticks();
}
};
class EventDispatcher
@ -38,12 +75,12 @@ public:
void flush();
void poll();
void addEvent(const SimpleCallback& callback, bool pushFront = false);
void scheduleEvent(const SimpleCallback& callback, int delay);
EventPtr addEvent(const SimpleCallback& callback, bool pushFront = false);
ScheduledEventPtr scheduleEvent(const SimpleCallback& callback, int delay);
private:
std::list<SimpleCallback> m_eventList;
std::priority_queue<ScheduledEvent> m_scheduledEventList;
std::list<EventPtr> m_eventList;
std::priority_queue<ScheduledEventPtr, std::vector<ScheduledEventPtr>, lessScheduledEvent> m_scheduledEventList;
};
extern EventDispatcher g_dispatcher;

View File

@ -43,6 +43,14 @@ void Application::registerLuaFunctions()
g_lua.bindGlobalFunction("colortostring", [](const Color& v) { return Fw::tostring(v); });
g_lua.bindGlobalFunction("sizetostring", [](const Size& v) { return Fw::tostring(v); });
// Event
g_lua.registerClass<Event>();
g_lua.bindClassMemberFunction<Event>("isCanceled", &Event::isCanceled);
g_lua.bindClassMemberFunction<Event>("isExecuted", &Event::isExecuted);
// ScheduledEvent
g_lua.registerClass<ScheduledEvent, Event>();
// UIWidget
g_lua.registerClass<UIWidget>();
g_lua.bindClassStaticFunction<UIWidget>("create", []{ return UIWidgetPtr(new UIWidget); });

View File

@ -119,4 +119,4 @@ T LuaObject::getLuaField(const std::string& key) {
return g_lua.polymorphicPop<T>();
}
#endif
#endif

View File

@ -351,16 +351,12 @@ 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()};
AdjustWindowRectEx(&windowRect, WS_OVERLAPPEDWINDOW, FALSE, WS_EX_APPWINDOW | WS_EX_WINDOWEDGE);
MoveWindow(m_window, windowRect.left, windowRect.top, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, TRUE);
MoveWindow(m_window, pos.x, pos.y, m_size.width(), m_size.height(), 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()};
AdjustWindowRectEx(&windowRect, WS_OVERLAPPEDWINDOW, FALSE, WS_EX_APPWINDOW | WS_EX_WINDOWEDGE);
MoveWindow(m_window, windowRect.left, windowRect.top, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, TRUE);
MoveWindow(m_window, m_pos.x, m_pos.y, size.width(), size.height(), TRUE);
}
void WIN32Window::show()
@ -400,7 +396,6 @@ void WIN32Window::poll()
LRESULT WIN32Window::windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
m_inputEvent.reset();
switch(uMsg)
{
case WM_ACTIVATE: {
@ -411,6 +406,8 @@ LRESULT WIN32Window::windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar
if(wParam >= 32 && wParam <= 255) {
m_inputEvent.reset(Fw::KeyTextInputEvent);
m_inputEvent.keyText = wParam;
if(m_onInputEvent)
m_onInputEvent(m_inputEvent);
}
break;
}
@ -429,31 +426,43 @@ LRESULT WIN32Window::windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar
case WM_LBUTTONDOWN: {
m_inputEvent.reset(Fw::MousePressInputEvent);
m_inputEvent.mouseButton = Fw::MouseLeftButton;
if(m_onInputEvent)
m_onInputEvent(m_inputEvent);
break;
}
case WM_LBUTTONUP: {
m_inputEvent.reset(Fw::MouseReleaseInputEvent);
m_inputEvent.mouseButton = Fw::MouseLeftButton;
if(m_onInputEvent)
m_onInputEvent(m_inputEvent);
break;
}
case WM_MBUTTONDOWN: {
m_inputEvent.reset(Fw::MousePressInputEvent);
m_inputEvent.mouseButton = Fw::MouseMidButton;
if(m_onInputEvent)
m_onInputEvent(m_inputEvent);
break;
}
case WM_MBUTTONUP: {
m_inputEvent.reset(Fw::MouseReleaseInputEvent);
m_inputEvent.mouseButton = Fw::MouseMidButton;
if(m_onInputEvent)
m_onInputEvent(m_inputEvent);
break;
}
case WM_RBUTTONDOWN: {
m_inputEvent.reset(Fw::MousePressInputEvent);
m_inputEvent.mouseButton = Fw::MouseRightButton;
if(m_onInputEvent)
m_onInputEvent(m_inputEvent);
break;
}
case WM_RBUTTONUP: {
m_inputEvent.reset(Fw::MouseReleaseInputEvent);
m_inputEvent.mouseButton = Fw::MouseRightButton;
if(m_onInputEvent)
m_onInputEvent(m_inputEvent);
break;
}
case WM_MOUSEMOVE: {
@ -461,11 +470,15 @@ LRESULT WIN32Window::windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar
Point newMousePos(LOWORD(lParam), HIWORD(lParam));
m_inputEvent.mouseMoved = newMousePos - m_inputEvent.mousePos;
m_inputEvent.mousePos = newMousePos;
if(m_onInputEvent)
m_onInputEvent(m_inputEvent);
break;
}
case WM_MOUSEWHEEL: {
m_inputEvent.mouseButton = Fw::MouseMidButton;
m_inputEvent.wheelDirection = HIWORD(wParam) > 0 ? Fw::MouseWheelUp : Fw::MouseWheelDown;
if(m_onInputEvent)
m_onInputEvent(m_inputEvent);
break;
}
case WM_MOVE: {
@ -494,8 +507,6 @@ LRESULT WIN32Window::windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
if(m_onInputEvent && m_inputEvent.type != Fw::NoInputEvent)
m_onInputEvent(m_inputEvent);
return 0;
}

View File

@ -193,26 +193,27 @@ void UIWidget::removeChild(const UIWidgetPtr& child)
void UIWidget::focusChild(const UIWidgetPtr& child, Fw::FocusReason reason)
{
if(child == m_focusedChild)
return;
if(child && !hasChild(child)) {
logError("Attempt to focus an unknown child in a UIWidget");
logError("attempt to focus an unknown child in a UIWidget");
return;
}
if(child != m_focusedChild) {
UIWidgetPtr oldFocused = m_focusedChild;
m_focusedChild = child;
UIWidgetPtr oldFocused = m_focusedChild;
m_focusedChild = child;
if(child) {
child->setLastFocusReason(reason);
child->updateState(Fw::FocusState);
child->updateState(Fw::ActiveState);
}
if(child) {
child->setLastFocusReason(reason);
child->updateState(Fw::FocusState);
child->updateState(Fw::ActiveState);
}
if(oldFocused) {
oldFocused->setLastFocusReason(reason);
oldFocused->updateState(Fw::FocusState);
oldFocused->updateState(Fw::ActiveState);
}
if(oldFocused) {
oldFocused->setLastFocusReason(reason);
oldFocused->updateState(Fw::FocusState);
oldFocused->updateState(Fw::ActiveState);
}
}
@ -389,6 +390,8 @@ void UIWidget::addAnchor(Fw::AnchorEdge anchoredEdge, const std::string& hookedW
{
if(UIAnchorLayoutPtr anchorLayout = getAnchoredLayout())
anchorLayout->addAnchor(asUIWidget(), anchoredEdge, hookedWidgetId, hookedEdge);
else
logError("cannot add anchors to widget ", m_id, ": the parent doesn't use anchors layout");
}
void UIWidget::centerIn(const std::string& hookedWidgetId)
@ -396,7 +399,8 @@ void UIWidget::centerIn(const std::string& hookedWidgetId)
if(UIAnchorLayoutPtr anchorLayout = getAnchoredLayout()) {
anchorLayout->addAnchor(asUIWidget(), Fw::AnchorHorizontalCenter, hookedWidgetId, Fw::AnchorHorizontalCenter);
anchorLayout->addAnchor(asUIWidget(), Fw::AnchorVerticalCenter, hookedWidgetId, Fw::AnchorVerticalCenter);
}
} else
logError("cannot add anchors to widget ", m_id, ": the parent doesn't use anchors layout");
}
void UIWidget::fill(const std::string& hookedWidgetId)
@ -406,7 +410,8 @@ void UIWidget::fill(const std::string& hookedWidgetId)
anchorLayout->addAnchor(asUIWidget(), Fw::AnchorRight, hookedWidgetId, Fw::AnchorRight);
anchorLayout->addAnchor(asUIWidget(), Fw::AnchorTop, hookedWidgetId, Fw::AnchorTop);
anchorLayout->addAnchor(asUIWidget(), Fw::AnchorBottom, hookedWidgetId, Fw::AnchorBottom);
}
} else
logError("cannot add anchors to widget ", m_id, ": the parent doesn't use anchors layout");
}
void UIWidget::breakAnchors()
@ -732,17 +737,10 @@ Rect UIWidget::getChildrenRect()
UIAnchorLayoutPtr UIWidget::getAnchoredLayout()
{
UIWidgetPtr parent = getParent();
if(!parent) {
logError("cannot add anchors to widget ", m_id, ": there is no parent");
if(!parent)
return nullptr;
}
UIAnchorLayoutPtr anchorLayout = parent->getLayout()->asUIAnchorLayout();
if(!anchorLayout) {
logError("cannot add anchors to widget ", m_id, ": the parent doesn't use anchors layout");
return nullptr;
}
return anchorLayout;
return parent->getLayout()->asUIAnchorLayout();
}
UIWidgetPtr UIWidget::getRootParent()
@ -1190,38 +1188,38 @@ bool UIWidget::propagateOnKeyUp(uchar keyCode, int keyboardModifiers)
bool UIWidget::propagateOnMousePress(const Point& mousePos, Fw::MouseButton button)
{
// do a backup of children list, because it may change while looping it
UIWidgetList children;
UIWidgetPtr clickedChild;
for(const UIWidgetPtr& child : m_children) {
// events on hidden or disabled widgets are discarded
if(!child->isExplicitlyEnabled() || !child->isExplicitlyVisible())
continue;
// mouse press events only go to children that contains the mouse position
if(child->containsPoint(mousePos) && child == getChildByPos(mousePos))
children.push_back(child);
if(child->containsPoint(mousePos) && child == getChildByPos(mousePos)) {
clickedChild = child;
break;
}
}
for(const UIWidgetPtr& child : children) {
// when a focusable item is focused it must gain focus
if(child->isFocusable())
focusChild(child, Fw::MouseFocusReason);
if(clickedChild) {
// focusable child gains focus when clicked
if(clickedChild->isFocusable())
focusChild(clickedChild, Fw::MouseFocusReason);
bool mustEnd = child->propagateOnMousePress(mousePos, button);
if(button == Fw::MouseLeftButton && !child->isPressed()) {
UIWidgetPtr clickedChild = child->getChildByPos(mousePos);
if(!clickedChild || clickedChild->isPhantom())
child->setPressed(true);
}
if(mustEnd)
// stop propagating if the child accept the event
if(clickedChild->propagateOnMousePress(mousePos, button))
return true;
}
if(!isPhantom())
return onMousePress(mousePos, button);
else
return false;
// only non phatom widgets receives mouse press events
if(!isPhantom()) {
onMousePress(mousePos, button);
if(button == Fw::MouseLeftButton && !isPressed())
setPressed(true);
return true;
}
return false;
}
void UIWidget::propagateOnMouseRelease(const Point& mousePos, Fw::MouseButton button)
@ -1239,12 +1237,12 @@ void UIWidget::propagateOnMouseRelease(const Point& mousePos, Fw::MouseButton bu
for(const UIWidgetPtr& child : children) {
child->propagateOnMouseRelease(mousePos, button);
if(child->isPressed() && button == Fw::MouseLeftButton)
child->setPressed(false);
}
onMouseRelease(mousePos, button);
if(isPressed() && button == Fw::MouseLeftButton)
setPressed(false);
}
bool UIWidget::propagateOnMouseMove(const Point& mousePos, const Point& mouseMoved)

View File

@ -44,17 +44,14 @@ Creature::Creature() : Thing()
m_showVolatileSquare = false;
m_showStaticSquare = false;
m_direction = Otc::South;
m_walkTimePerPixel = 1000.0/32.0;
m_walking = false;
m_preWalking = false;
m_walkInterval = 0;
m_walkTurnDirection = Otc::InvalidDirection;
m_skull = Otc::SkullNone;
m_shield = Otc::ShieldNone;
m_emblem = Otc::EmblemNone;
m_shieldBlink = false;
m_showShieldTexture = true;
m_informationFont = g_fonts.getFont("verdana-11px-rounded");
}
@ -187,110 +184,133 @@ void Creature::drawInformation(int x, int y, bool useGray, const Rect& visibleRe
}
}
void Creature::walk(const Position& oldPos, const Position& newPos, bool preWalk)
void Creature::turn(Otc::Direction direction)
{
// if is not walking change the direction right away
if(!m_walking)
setDirection(direction);
// schedules to set the new direction when walk ends
else
m_walkTurnDirection = direction;
}
void Creature::walk(const Position& oldPos, const Position& newPos)
{
// get walk direction
Otc::Direction direction = oldPos.getDirectionFromPosition(newPos);
// already pre walking to the same direction
if(m_preWalking && preWalk && direction == m_direction)
return;
// pre walking was already going on, just change to normal waking
if(m_preWalking && !preWalk && direction == m_direction) {
m_preWalking = false;
m_walking = true;
updateWalk();
return;
}
// set current walking direction
setDirection(direction);
// diagonal walking lasts 3 times more.
int walkTimeFactor = 1;
if(direction == Otc::NorthWest || direction == Otc::NorthEast || direction == Otc::SouthWest || direction == Otc::SouthEast)
walkTimeFactor = 3;
// calculate walk interval
int groundSpeed = g_map.getTile(oldPos)->getGroundSpeed();
float walkInterval = 1000.0 * (float)groundSpeed / m_speed;
walkInterval = (walkInterval == 0) ? 1000 : walkInterval;
walkInterval = std::ceil(walkInterval / g_game.getServerBeat()) * g_game.getServerBeat();
m_walkTimePerPixel = walkInterval / 32.0;
m_walkOffset = Point();
m_walkStart = g_clock.ticks();
m_walkEnd = m_walkStart + walkInterval * walkTimeFactor;
// starts counting walk
m_walking = true;
m_preWalking = preWalk;
m_turnDirection = m_direction;
updateWalk();
m_walkTimer.restart();
// calculates walk interval
float walkInterval = 1000;
int groundSpeed = g_map.getTile(oldPos)->getGroundSpeed();
if(groundSpeed != 0)
walkInterval = (1000.0f * groundSpeed) / m_speed;
// diagonal walking lasts 3 times more.
//if(direction == Otc::NorthWest || direction == Otc::NorthEast || direction == Otc::SouthWest || direction == Otc::SouthEast)
// walkInterval *= 3;
m_walkInterval = (walkInterval / g_game.getServerBeat()) * g_game.getServerBeat();
// no direction needs to be changed when the walk ends
m_walkTurnDirection = Otc::InvalidDirection;
// starts updating walk
nextWalkUpdate();
}
void Creature::turn(Otc::Direction direction)
void Creature::stopWalk()
{
if(!m_walking)
setDirection(direction);
else
m_turnDirection = direction;
return;
// reset walk animation states
updateWalkAnimation(0);
updateWalkOffset(0);
// stops the walk right away
terminateWalk();
}
void Creature::updateWalkAnimation(int totalPixelsWalked)
{
// update outfit animation
if(m_outfit.getCategory() == ThingsType::Creature) {
if(totalPixelsWalked == 32 || totalPixelsWalked == 0 || m_type->dimensions[ThingType::AnimationPhases] <= 1)
m_animation = 0;
else if(m_type->dimensions[ThingType::AnimationPhases] > 1)
m_animation = 1 + ((totalPixelsWalked * 4) / Map::NUM_TILE_PIXELS) % (m_type->dimensions[ThingType::AnimationPhases] - 1);
}
}
void Creature::updateWalkOffset(int totalPixelsWalked)
{
m_walkOffset = Point(0,0);
if(m_direction == Otc::North || m_direction == Otc::NorthEast || m_direction == Otc::NorthWest)
m_walkOffset.y = 32 - totalPixelsWalked;
else if(m_direction == Otc::South || m_direction == Otc::SouthEast || m_direction == Otc::SouthWest)
m_walkOffset.y = totalPixelsWalked - 32;
if(m_direction == Otc::East || m_direction == Otc::NorthEast || m_direction == Otc::SouthEast)
m_walkOffset.x = totalPixelsWalked - 32;
else if(m_direction == Otc::West || m_direction == Otc::NorthWest || m_direction == Otc::SouthWest)
m_walkOffset.x = 32 - totalPixelsWalked;
}
void Creature::nextWalkUpdate()
{
// remove any previous scheduled walk updates
if(m_walkUpdateEvent)
m_walkUpdateEvent->cancel();
// do the update
updateWalk();
// schedules next update
if(m_walking) {
auto self = asCreature();
m_walkUpdateEvent = g_dispatcher.scheduleEvent([self] {
self->m_walkUpdateEvent = nullptr;
self->nextWalkUpdate();
}, m_walkInterval / 32);
}
}
void Creature::updateWalk()
{
if(!m_walking)
return;
float walkTicksPerPixel = m_walkInterval / 32.0f;
int totalPixelsWalked = std::min(m_walkTimer.ticksElapsed() / walkTicksPerPixel, 32.0f);
int elapsedTicks = g_clock.ticksElapsed(m_walkStart);
int totalPixelsWalked = std::min((int)round(elapsedTicks / m_walkTimePerPixel), 32);
// update walk animation and offsets
updateWalkAnimation(totalPixelsWalked);
updateWalkOffset(totalPixelsWalked);
if(!m_preWalking) {
if(m_direction == Otc::North || m_direction == Otc::NorthEast || m_direction == Otc::NorthWest)
m_walkOffset.y = 32 - totalPixelsWalked;
else if(m_direction == Otc::South || m_direction == Otc::SouthEast || m_direction == Otc::SouthWest)
m_walkOffset.y = totalPixelsWalked - 32;
if(m_direction == Otc::East || m_direction == Otc::NorthEast || m_direction == Otc::SouthEast)
m_walkOffset.x = totalPixelsWalked - 32;
else if(m_direction == Otc::West || m_direction == Otc::NorthWest || m_direction == Otc::SouthWest)
m_walkOffset.x = 32 - totalPixelsWalked;
} else {
if(m_direction == Otc::North || m_direction == Otc::NorthEast || m_direction == Otc::NorthWest)
m_walkOffset.y = -totalPixelsWalked;
else if(m_direction == Otc::South || m_direction == Otc::SouthEast || m_direction == Otc::SouthWest)
m_walkOffset.y = totalPixelsWalked;
if(m_direction == Otc::East || m_direction == Otc::NorthEast || m_direction == Otc::SouthEast)
m_walkOffset.x = totalPixelsWalked;
else if(m_direction == Otc::West || m_direction == Otc::NorthWest || m_direction == Otc::SouthWest)
m_walkOffset.x = -totalPixelsWalked;
}
if(m_outfit.getCategory() == ThingsType::Creature) {
if(totalPixelsWalked == 32 || m_type->dimensions[ThingType::AnimationPhases] <= 1)
m_animation = 0;
else if(m_type->dimensions[ThingType::AnimationPhases] > 1)
m_animation = 1 + totalPixelsWalked * 4 / Map::NUM_TILE_PIXELS % (m_type->dimensions[ThingType::AnimationPhases] - 1);
}
if(g_clock.ticks() > m_walkEnd) {
cancelWalk(m_turnDirection);
} else
g_dispatcher.scheduleEvent(std::bind(&Creature::updateWalk, asCreature()), m_walkTimePerPixel);
// terminate walk
if(m_walking && m_walkTimer.ticksElapsed() >= m_walkInterval)
terminateWalk();
}
void Creature::cancelWalk(Otc::Direction direction, bool force)
void Creature::terminateWalk()
{
if(force) {
m_walkOffset = Point();
m_preWalking = false;
} else if(!m_preWalking)
m_walkOffset = Point();
// remove any scheduled walk update
if(m_walkUpdateEvent) {
m_walkUpdateEvent->cancel();
m_walkUpdateEvent = nullptr;
}
// now the walk has ended, do any scheduled turn
if(m_walkTurnDirection != Otc::InvalidDirection) {
setDirection(m_walkTurnDirection);
m_walkTurnDirection = Otc::InvalidDirection;
}
m_walking = false;
if(direction != Otc::InvalidDirection)
setDirection(direction);
m_animation = 0;
}
void Creature::setName(const std::string& name)

View File

@ -25,6 +25,8 @@
#include "thing.h"
#include "outfit.h"
#include <framework/core/declarations.h>
#include <framework/core/timer.h>
#include <framework/graphics/fontmanager.h>
class Creature : public Thing
@ -79,18 +81,21 @@ public:
ThingType *getType();
// walk related
void walk(const Position& oldPos, const Position& newPos, bool preWalk = false);
void turn(Otc::Direction direction);
void cancelWalk(Otc::Direction direction = Otc::InvalidDirection, bool force = false);
virtual void walk(const Position& oldPos, const Position& newPos);
virtual void stopWalk();
Point getWalkOffset() { return m_walkOffset; }
bool isWalking() { return m_walking; }
bool isPreWalking() { return m_preWalking; }
CreaturePtr asCreature() { return std::static_pointer_cast<Creature>(shared_from_this()); }
protected:
void updateWalk();
virtual void updateWalkAnimation(int totalPixelsWalked);
virtual void updateWalkOffset(int totalPixelsWalked);
virtual void nextWalkUpdate();
virtual void updateWalk();
virtual void terminateWalk();
std::string m_name;
Size m_nameSize;
@ -98,7 +103,7 @@ protected:
Otc::Direction m_direction;
Outfit m_outfit;
Light m_light;
uint16 m_speed;
int m_speed;
uint8 m_skull, m_shield, m_emblem;
TexturePtr m_skullTexture, m_shieldTexture, m_emblemTexture;
bool m_showShieldTexture, m_shieldBlink;
@ -109,11 +114,13 @@ protected:
FontPtr m_informationFont;
Color m_informationColor;
ticks_t m_walkStart, m_walkEnd;
bool m_walking, m_preWalking;
float m_walkTimePerPixel;
// walk related
Timer m_walkTimer;
int m_walkInterval;
bool m_walking;
ScheduledEventPtr m_walkUpdateEvent;
Point m_walkOffset;
Otc::Direction m_turnDirection;
Otc::Direction m_walkTurnDirection;
};
class Npc : public Creature {

View File

@ -35,7 +35,6 @@ Game g_game;
void Game::loginWorld(const std::string& account, const std::string& password, const std::string& worldHost, int worldPort, const std::string& characterName)
{
m_online = false;
m_dead = false;
m_selectedThing = nullptr;
m_protocolGame = ProtocolGamePtr(new ProtocolGame);
@ -49,7 +48,7 @@ void Game::cancelLogin()
void Game::logout(bool force)
{
if(!m_protocolGame || !m_online)
if(!m_protocolGame || !isOnline())
return;
m_protocolGame->sendLogout();
@ -77,7 +76,6 @@ void Game::processConnectionError(const boost::system::error_code& error)
void Game::processLogin(const LocalPlayerPtr& localPlayer, int serverBeat)
{
m_localPlayer = localPlayer;
m_online = true;
m_serverBeat = serverBeat;
// NOTE: the entire map description is not known yet
@ -86,17 +84,18 @@ void Game::processLogin(const LocalPlayerPtr& localPlayer, int serverBeat)
void Game::processLogout()
{
if(m_online) {
if(isOnline()) {
g_lua.callGlobalField("Game", "onLogout", m_localPlayer);
m_localPlayer.reset();
m_online = false;
m_localPlayer = nullptr;
}
if(m_protocolGame) {
m_protocolGame->disconnect();
m_protocolGame.reset();
m_protocolGame = nullptr;
}
g_map.clean();
}
void Game::processDeath()
@ -145,8 +144,7 @@ void Game::processCreatureMove(const CreaturePtr& creature, const Position& oldP
// teleport
} else {
// stop walking on teleport
if(creature->isWalking() || creature->isPreWalking())
creature->cancelWalk();
creature->stopWalk();
}
}
@ -158,7 +156,8 @@ void Game::processAttackCancel()
void Game::processWalkCancel(Otc::Direction direction)
{
m_localPlayer->cancelWalk(direction, true);
logTraceDebug();
m_localPlayer->cancelWalk(direction);
}
void Game::walk(Otc::Direction direction)
@ -208,7 +207,7 @@ void Game::forceWalk(Otc::Direction direction)
void Game::turn(Otc::Direction direction)
{
if(!m_online)
if(!isOnline())
return;
switch(direction) {
@ -229,7 +228,7 @@ void Game::turn(Otc::Direction direction)
void Game::look(const ThingPtr& thing)
{
if(!m_online || !thing || !checkBotProtection())
if(!isOnline() || !thing || !checkBotProtection())
return;
int stackpos = getThingStackpos(thing);
@ -239,7 +238,7 @@ void Game::look(const ThingPtr& thing)
void Game::open(const ThingPtr& thing, int containerId)
{
if(!m_online || !thing || !checkBotProtection())
if(!isOnline() || !thing || !checkBotProtection())
return;
int stackpos = getThingStackpos(thing);
@ -249,9 +248,11 @@ void Game::open(const ThingPtr& thing, int containerId)
void Game::use(const ThingPtr& thing)
{
if(!m_online || !thing || !checkBotProtection())
if(!isOnline() || !thing || !checkBotProtection())
return;
m_localPlayer->lockWalk();
int stackpos = getThingStackpos(thing);
if(stackpos != -1)
m_protocolGame->sendUseItem(thing->getPos(), thing->getId(), stackpos, 0);
@ -259,7 +260,7 @@ void Game::use(const ThingPtr& thing)
void Game::useWith(const ThingPtr& fromThing, const ThingPtr& toThing)
{
if(!m_online || !fromThing || !toThing || !checkBotProtection())
if(!isOnline() || !fromThing || !toThing || !checkBotProtection())
return;
Position pos = fromThing->getPos();
@ -267,6 +268,8 @@ void Game::useWith(const ThingPtr& fromThing, const ThingPtr& toThing)
if(fromStackpos == -1)
return;
m_localPlayer->lockWalk();
if(CreaturePtr creature = toThing->asCreature()) {
m_protocolGame->sendUseOnCreature(pos, fromThing->getId(), fromStackpos, creature->getId());
} else {
@ -280,9 +283,11 @@ void Game::useWith(const ThingPtr& fromThing, const ThingPtr& toThing)
void Game::useInventoryItem(int itemId, const ThingPtr& toThing)
{
if(!m_online || !toThing || !checkBotProtection())
if(!isOnline() || !toThing || !checkBotProtection())
return;
m_localPlayer->lockWalk();
Position pos = Position(0xFFFF, 0, 0); // means that is a item in inventory
int toStackpos = getThingStackpos(toThing);
if(toStackpos == -1)
@ -297,9 +302,11 @@ void Game::useInventoryItem(int itemId, const ThingPtr& toThing)
void Game::attack(const CreaturePtr& creature)
{
if(!m_online || !creature || !checkBotProtection())
if(!isOnline() || !creature || !checkBotProtection())
return;
m_localPlayer->lockWalk();
if(m_localPlayer->isFollowing())
cancelFollow();
@ -309,13 +316,15 @@ void Game::attack(const CreaturePtr& creature)
void Game::cancelAttack()
{
m_localPlayer->lockWalk();
m_localPlayer->setAttackingCreature(nullptr);
m_protocolGame->sendAttack(0);
}
void Game::follow(const CreaturePtr& creature)
{
if(!m_online || !creature || !checkBotProtection())
if(!isOnline() || !creature || !checkBotProtection())
return;
if(m_localPlayer->isAttacking())
@ -333,7 +342,7 @@ void Game::cancelFollow()
void Game::rotate(const ThingPtr& thing)
{
if(!m_online || !thing || !checkBotProtection())
if(!isOnline() || !thing || !checkBotProtection())
return;
int stackpos = getThingStackpos(thing);
@ -361,42 +370,42 @@ void Game::talk(const std::string& message)
void Game::talkChannel(const std::string& speakTypeDesc, int channelId, const std::string& message)
{
if(!m_online || !checkBotProtection())
if(!isOnline() || !checkBotProtection())
return;
m_protocolGame->sendTalk(speakTypeDesc, channelId, "", message);
}
void Game::talkPrivate(const std::string& speakTypeDesc, const std::string& receiver, const std::string& message)
{
if(!m_online || !checkBotProtection())
if(!isOnline() || !checkBotProtection())
return;
m_protocolGame->sendTalk(speakTypeDesc, 0, receiver, message);
}
void Game::requestChannels()
{
if(!m_online || !checkBotProtection())
if(!isOnline() || !checkBotProtection())
return;
m_protocolGame->sendGetChannels();
}
void Game::joinChannel(int channelId)
{
if(!m_online || !checkBotProtection())
if(!isOnline() || !checkBotProtection())
return;
m_protocolGame->sendJoinChannel(channelId);
}
void Game::leaveChannel(int channelId)
{
if(!m_online || !checkBotProtection())
if(!isOnline() || !checkBotProtection())
return;
m_protocolGame->sendLeaveChannel(channelId);
}
void Game::closeNpcChannel()
{
if(!m_online || !checkBotProtection())
if(!isOnline() || !checkBotProtection())
return;
m_protocolGame->sendCloseNpcChannel();
}
@ -404,21 +413,21 @@ void Game::closeNpcChannel()
void Game::partyInvite(int creatureId)
{
if(!m_online || !checkBotProtection())
if(!isOnline() || !checkBotProtection())
return;
m_protocolGame->sendInviteToParty(creatureId);
}
void Game::partyJoin(int creatureId)
{
if(!m_online || !checkBotProtection())
if(!isOnline() || !checkBotProtection())
return;
m_protocolGame->sendJoinParty(creatureId);
}
void Game::partyRevokeInvitation(int creatureId)
{
if(!m_online || !checkBotProtection())
if(!isOnline() || !checkBotProtection())
return;
m_protocolGame->sendRevokeInvitation(creatureId);
}
@ -426,49 +435,49 @@ void Game::partyRevokeInvitation(int creatureId)
void Game::partyPassLeadership(int creatureId)
{
if(!m_online || !checkBotProtection())
if(!isOnline() || !checkBotProtection())
return;
m_protocolGame->sendPassLeadership(creatureId);
}
void Game::partyLeave()
{
if(!m_online || !checkBotProtection())
if(!isOnline() || !checkBotProtection())
return;
m_protocolGame->sendLeaveParty();
}
void Game::partyShareExperience(bool active)
{
if(!m_online || !checkBotProtection())
if(!isOnline() || !checkBotProtection())
return;
m_protocolGame->sendShareExperience(active, 0);
}
void Game::requestOutfit()
{
if(!m_online || !checkBotProtection())
if(!isOnline() || !checkBotProtection())
return;
m_protocolGame->sendGetOutfit();
}
void Game::setOutfit(const Outfit& outfit)
{
if(!m_online || !checkBotProtection())
if(!isOnline() || !checkBotProtection())
return;
m_protocolGame->sendSetOutfit(outfit);
}
void Game::addVip(const std::string& name)
{
if(!m_online || name.empty() || !checkBotProtection())
if(!isOnline() || name.empty() || !checkBotProtection())
return;
m_protocolGame->sendAddVip(name);
}
void Game::removeVip(int playerId)
{
if(!m_online || !checkBotProtection())
if(!isOnline() || !checkBotProtection())
return;
m_protocolGame->sendRemoveVip(playerId);
}

View File

@ -102,7 +102,7 @@ public:
bool checkBotProtection();
bool isOnline() { return m_online; }
bool isOnline() { return !!m_localPlayer; }
bool isDead() { return m_dead; }
void setSelectedThing(const ThingPtr& thing) { m_selectedThing = thing; }
@ -118,7 +118,6 @@ public:
private:
LocalPlayerPtr m_localPlayer;
ProtocolGamePtr m_protocolGame;
bool m_online;
bool m_dead;
int m_serverBeat;
ThingPtr m_selectedThing;

View File

@ -25,24 +25,75 @@
#include "game.h"
#include "tile.h"
LocalPlayer::LocalPlayer()
{
m_preWalking = false;
m_canReportBugs = false;
m_known = false;
m_walkLocked = false;
m_lastPrewalkDone = true;
m_icons = 0;
}
void LocalPlayer::lockWalk()
{
// prevents double locks
if(m_walkLocked)
return;
m_walkLocked = true;
m_walkLockTimer.restart();
}
void LocalPlayer::walk(const Position& oldPos, const Position& newPos)
{
Otc::Direction direction = oldPos.getDirectionFromPosition(newPos);
// a prewalk was going on
if(m_preWalking) {
// switch to normal walking
m_preWalking = false;
m_lastPrewalkDone = true;
// if is to the destination, updates it preserving the animation
if(newPos == m_lastPrewalkDestionation) {
// walk started by prewalk could already be finished by now
updateWalk();
// was to another direction, replace the walk
} else
Creature::walk(oldPos, newPos);
} else
Creature::walk(oldPos, newPos);
}
void LocalPlayer::preWalk(Otc::Direction direction)
{
// we're not walking, so start a client walk.
// start walking to direction
Position newPos = m_pos + Position::getPosFromDirection(direction);
walk(m_pos, newPos, true);
m_preWalking = true;
m_lastPrewalkDone = false;
m_lastPrewalkDestionation = newPos;
Creature::walk(m_pos, newPos);
}
bool LocalPlayer::canWalk(Otc::Direction direction)
{
if(m_walking || (m_preWalking && g_clock.ticksElapsed(m_walkEnd) < 1000))
// cannot walk while already walking
if(m_walking)
return false;
// avoid doing more walks than wanted when receiving a lot of walks from server
if(!m_lastPrewalkDone)
return false;
// cannot walk while locked
if(m_walkLocked && m_walkLockTimer.ticksElapsed() <= WALK_LOCK_INTERVAL)
return false;
else
m_walkLocked = false;
// check for blockable tiles in the walk direction
TilePtr tile = g_map.getTile(m_pos + Position::getPosFromDirection(direction));
if(!tile)
return false;
if(!tile->isWalkable()) {
if(!tile || !tile->isWalkable()) {
g_game.processTextMessage("statusSmall", "Sorry, not possible.");
return false;
}
@ -50,6 +101,63 @@ bool LocalPlayer::canWalk(Otc::Direction direction)
return true;
}
void LocalPlayer::cancelWalk(Otc::Direction direction)
{
// only cancel client side walks
if(m_walking && m_preWalking)
stopWalk();
m_lastPrewalkDone = true;
// turn to the cancel direction
if(direction != Otc::InvalidDirection)
setDirection(direction);
}
void LocalPlayer::stopWalk()
{
Creature::stopWalk();
m_lastPrewalkDestionation = Position();
}
void LocalPlayer::updateWalkOffset(int totalPixelsWalked)
{
// pre walks offsets are calculated in the oposite direction
if(m_preWalking) {
m_walkOffset = Point(0,0);
if(m_direction == Otc::North || m_direction == Otc::NorthEast || m_direction == Otc::NorthWest)
m_walkOffset.y = -totalPixelsWalked;
else if(m_direction == Otc::South || m_direction == Otc::SouthEast || m_direction == Otc::SouthWest)
m_walkOffset.y = totalPixelsWalked;
if(m_direction == Otc::East || m_direction == Otc::NorthEast || m_direction == Otc::SouthEast)
m_walkOffset.x = totalPixelsWalked;
else if(m_direction == Otc::West || m_direction == Otc::NorthWest || m_direction == Otc::SouthWest)
m_walkOffset.x = -totalPixelsWalked;
} else
Creature::updateWalkOffset(totalPixelsWalked);
}
void LocalPlayer::updateWalk()
{
float walkTicksPerPixel = m_walkInterval / 32.0f;
int totalPixelsWalked = std::min(m_walkTimer.ticksElapsed() / walkTicksPerPixel, 32.0f);
// update walk animation and offsets
updateWalkAnimation(totalPixelsWalked);
updateWalkOffset(totalPixelsWalked);
// terminate walk only when client and server side walk are complated
if(m_walking && !m_preWalking && m_walkTimer.ticksElapsed() >= m_walkInterval)
terminateWalk();
}
void LocalPlayer::terminateWalk()
{
Creature::terminateWalk();
m_preWalking = false;
}
void LocalPlayer::setAttackingCreature(const CreaturePtr& creature)
{
// clear current attacking creature

View File

@ -27,13 +27,18 @@
class LocalPlayer : public Player
{
enum {
WALK_LOCK_INTERVAL = 250
};
public:
LocalPlayer();
void setCanReportBugs(uint8 canReportBugs) { m_canReportBugs = (canReportBugs != 0); }
void setSkill(Otc::Skill skill, Otc::SkillType skillType, int value) { m_skills[skill][skillType] = value; }
void setStatistic(Otc::Statistic statistic, double value) { m_statistics[statistic] = value; }
void setAttackingCreature(const CreaturePtr& creature);
void setFollowingCreature(const CreaturePtr& creature);
void setIcons(Otc::PlayerIcons icons) { m_icons = icons; }
void setIcons(int icons) { m_icons = icons; }
void setKnown(bool known) { m_known = known; }
bool getCanReportBugs() { return m_canReportBugs; }
@ -41,14 +46,19 @@ public:
double getStatistic(Otc::Statistic statistic) { return m_statistics[statistic]; }
CreaturePtr getAttackingCreature() { return m_attackingCreature; }
CreaturePtr getFollowingCreature() { return m_followingCreature; }
Otc::PlayerIcons getIcons() { return m_icons; }
int getIcons() { return m_icons; }
bool isKnown() { return m_known; }
bool isAttacking() { return m_attackingCreature != nullptr; }
bool isFollowing() { return m_followingCreature != nullptr; }
void unlockWalk() { m_walkLocked = false; }
void lockWalk();
void walk(const Position& oldPos, const Position& newPos);
void preWalk(Otc::Direction direction);
bool canWalk(Otc::Direction direction);
void cancelWalk(Otc::Direction direction = Otc::InvalidDirection);
void stopWalk();
LocalPlayerPtr asLocalPlayer() { return std::static_pointer_cast<LocalPlayer>(shared_from_this()); }
@ -65,11 +75,24 @@ public:
double getSoul() { return getStatistic(Otc::Soul); }
double getStamina() { return getStatistic(Otc::Stamina); }
protected:
void updateWalkOffset(int totalPixelsWalked);
void updateWalk();
void terminateWalk();
private:
// walk related
bool m_preWalking;
bool m_lastPrewalkDone;
bool m_walkLocked;
Position m_lastPrewalkDestionation;
Timer m_walkLockTimer;
bool m_canReportBugs;
bool m_known;
CreaturePtr m_attackingCreature, m_followingCreature;
Otc::PlayerIcons m_icons;
CreaturePtr m_attackingCreature;
CreaturePtr m_followingCreature;
int m_icons;
int m_skills[Otc::LastSkill][Otc::LastSkillType];
double m_statistics[Otc::LastStatistic];
};

View File

@ -150,6 +150,11 @@ void Map::draw(const Rect& rect)
void Map::clean()
{
m_tiles.clear();
m_creatures.clear();
for(int i=0;i<MAX_Z-1;++i)
m_missilesAtFloor[i].clear();
m_animatedTexts.clear();
m_staticTexts.clear();
}
int Map::getFirstVisibleFloor()

View File

@ -83,8 +83,10 @@ private:
Light m_light;
Position m_centralPosition;
Size m_size, m_visibleSize;
Point m_centralOffset, m_drawOffset;
Size m_size;
Size m_visibleSize;
Point m_centralOffset;
Point m_drawOffset;
FrameBufferPtr m_framebuffer;
PainterShaderProgramPtr m_shaderProgram;

View File

@ -1141,7 +1141,7 @@ ThingPtr ProtocolGame::internalGetThing(InputMessage& msg)
if(emblem != -1)
creature->setEmblem(emblem);
creature->setPassable(passable);
creature->cancelWalk(direction);
creature->setDirection(direction);
if(creature == m_localPlayer) {
m_localPlayer->setKnown(true);