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

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