walk and key event system rework with some regressions

This commit is contained in:
Eduardo Bart 2012-01-15 19:19:52 -02:00
parent 9ec40f016d
commit 44a20222bb
52 changed files with 542 additions and 346 deletions

6
TODO
View File

@ -1,7 +1,6 @@
====================================================
High priority TODO in order (before first public disclose)
[bart] tab widgets
[bart] chat with tabs
[bart] scrollbar
[bart] scrollable widgets
@ -62,8 +61,10 @@ change win32 mouse cursor icon
[bart] review and make more error prone with more warnings
[bart] reapply anchor styles when adding new childs
[bart] ui text selection
[bart] find styles by scope
[bart] make set of background/icon/image width alone work
[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
== Client modules
[bart] make possible to reload modules
@ -73,11 +74,12 @@ change win32 mouse cursor icon
[bart] clean sprites cache periodically
[bart] create a shader manager
[bart] find a way to load map rendering styles
[bart] move redering of creatures names, skulls, etc to UI
[bart] cache screen creatures in a list on map
[bart] handle corrupt errors in dat/spr
[bart] remake spr/dat using OTML and image files
[bart] rework map tile rendering (cache visible tiles, etc)
[bart] minimap windows
[bart] minimap window
[bart] draw lights using shaders
[bart] limit FPS in options
[bart] resize map, right panel

View File

@ -8,7 +8,7 @@ function PingBar.init()
pingLabel:applyStyle({ ['anchors.left'] = 'prev.right',
['anchors.top'] = 'parent.top',
['margin-top'] = 12,
['margin-left'] = 10,
['margin-left'] = 20,
font = 'verdana-11px-rounded',
color = '#FE6500',
width = 120,

View File

@ -4,7 +4,7 @@ Module
author: OTClient team
website: https://github.com/edubart/otclient
autoLoad: false
autoLoad: true
autoLoadAntecedence: 1000
onLoad: |

View File

@ -108,16 +108,16 @@ function Terminal.init()
terminalWidget:setVisible(false)
terminalButton = TopMenu.addButton('terminalButton', 'Terminal (Ctrl + T)', '/core_styles/icons/terminal.png', Terminal.toggle)
Hotkeys.bind('Ctrl+T', Terminal.toggle)
Hotkeys.bindKeyDown('Ctrl+T', Terminal.toggle)
commandHistory = Settings.getList('terminal-history')
commandLineEdit = terminalWidget:getChildById('commandLineEdit')
Hotkeys.bind('Up', function() navigateCommand(1) end, commandLineEdit)
Hotkeys.bind('Down', function() navigateCommand(-1) end, commandLineEdit)
Hotkeys.bind('Tab', completeCommand, commandLineEdit)
Hotkeys.bind('Enter', doCommand, commandLineEdit)
Hotkeys.bind('Return', doCommand, commandLineEdit)
Hotkeys.bindKeyDown('Up', function() navigateCommand(1) end, commandLineEdit)
Hotkeys.bindKeyDown('Down', function() navigateCommand(-1) end, commandLineEdit)
Hotkeys.bindKeyDown('Tab', completeCommand, commandLineEdit)
Hotkeys.bindKeyDown('Enter', doCommand, commandLineEdit)
Hotkeys.bindKeyDown('Return', doCommand, commandLineEdit)
terminalBuffer = terminalWidget:getChildById('terminalBuffer')
Logger.setOnLog(onLog)
@ -126,7 +126,7 @@ end
function Terminal.terminate()
Settings.setList('terminal-history', commandHistory)
Hotkeys.unbind('Ctrl+T')
Hotkeys.unbindKeyDown('Ctrl+T')
Logger.setOnLog(nil)
terminalButton:destroy()
terminalButton = nil

View File

@ -6,7 +6,8 @@ local loadBox
local characterList
-- private functions
local function onCharactersWindowKeyPress(self, keyCode, keyText, keyboardModifiers)
local function onCharactersWindowKeyPress(self, keyCode, keyboardModifiers, wouldFilter)
if wouldFilter then return end
if keyboardModifiers == KeyboardNoModifier then
if keyCode == KeyUp then
characterList:focusPreviousChild(ActiveFocusReason)

View File

@ -57,7 +57,7 @@ end
-- public functions
function EnterGame.init()
enterGameButton = TopMenu.addButton('enterGameButton', 'Login (Ctrl + G)', '/core_styles/icons/login.png', EnterGame.openWindow)
Hotkeys.bind('Ctrl+G', EnterGame.openWindow)
Hotkeys.bindKeyDown('Ctrl+G', EnterGame.openWindow)
motdButton = TopMenu.addButton('motdButton', 'Message of the day', '/core_styles/icons/motd.png', EnterGame.displayMotd)
motdButton:hide()
enterGame = displayUI('entergame.otui')
@ -82,7 +82,7 @@ function EnterGame.init()
end
function EnterGame.terminate()
Hotkeys.unbind('Ctrl+G')
Hotkeys.unbindKeyDown('Ctrl+G')
enterGame:destroy()
enterGame = nil
enterGameButton:destroy()

View File

@ -25,11 +25,11 @@ function Options.init()
optionsWindow = displayUI('options.otui')
optionsWindow:setVisible(false)
optionsButton = TopMenu.addButton('settingsButton', 'Options (Ctrl+O)', '/core_styles/icons/settings.png', Options.toggle)
Hotkeys.bind('Ctrl+O', Options.toggle)
Hotkeys.bindKeyDown('Ctrl+O', Options.toggle)
end
function Options.terminate()
Hotkeys.unbind('Ctrl+O')
Hotkeys.unbindKeyDown('Ctrl+O')
optionsWindow:destroy()
optionsWindow = nil
optionsButton:destroy()

View File

@ -23,11 +23,11 @@ function TopMenu.init()
gameButtonsPanel = topMenu:getChildById('gameButtonsPanel')
TopMenu.addRightButton('logoutButton', 'Logout (Ctrl+Q)', '/core_styles/icons/logout.png', onLogout)
Hotkeys.bind('Ctrl+Q', onLogout)
Hotkeys.bindKeyDown('Ctrl+Q', onLogout)
end
function TopMenu.terminate()
Hotkeys.unbind('Ctrl+Q')
Hotkeys.unbindKeyDown('Ctrl+Q')
leftButtonsPanel = nil
rightButtonsPanel = nil
topMenu:destroy()

View File

@ -45,6 +45,15 @@ AlignTopCenter = 20
AlignBottomCenter = 24
AlignCenter = 48
North = 0
East = 1
South = 2
West = 3
NorthEast = 4
SouthEast = 5
SouthWest = 6
NorthWest = 7
KeyUnknown = 0
KeyEscape = 1

View File

@ -58,10 +58,10 @@ local function determineKeyComboDesc(keyCode, keyboardModifiers)
return translateKeyCombo(keyCombo)
end
local function onWidgetKeyPress(widget, keyCode, keyText, keyboardModifiers)
local function onWidgetKeyDown(widget, keyCode, keyboardModifiers)
if keyCode == KeyUnknown then return end
local keyComboDesc = determineKeyComboDesc(keyCode, keyboardModifiers)
local callback = widget.boundKeyCombos[keyComboDesc]
local callback = widget.boundKeyDownCombos[keyComboDesc]
if callback then
callback()
return true
@ -69,29 +69,57 @@ local function onWidgetKeyPress(widget, keyCode, keyText, keyboardModifiers)
return false
end
local function connectWidgetHotkeyEvent(widget)
if widget.boundKeyCombos then return end
local function onWidgetKeyPress(widget, keyCode, keyboardModifiers, wouldFilter)
local keyComboDesc = determineKeyComboDesc(keyCode, keyboardModifiers)
if keyCode == KeyUnknown then return end
local callback = widget.boundKeyPressCombos[keyComboDesc]
if callback then
callback()
return true
end
return false
end
local function connectKeyDownEvent(widget)
if widget.boundKeyDownCombos then return end
connect(widget, { onKeyDown = onWidgetKeyDown })
widget.boundKeyDownCombos = {}
end
local function connectKeyPressEvent(widget)
if widget.boundKeyPressCombos then return end
connect(widget, { onKeyPress = onWidgetKeyPress })
widget.boundKeyCombos = {}
widget.boundKeyPressCombos = {}
end
-- public functions
function Hotkeys.bind(keyComboDesc, callback, widget)
function Hotkeys.bindKeyDown(keyComboDesc, callback, widget)
widget = widget or rootWidget
connectWidgetHotkeyEvent(widget)
connectKeyDownEvent(widget)
local keyComboDesc = retranslateKeyComboDesc(keyComboDesc)
if keyComboDesc then
widget.boundKeyCombos[keyComboDesc] = callback
widget.boundKeyDownCombos[keyComboDesc] = callback
else
error('key combo \'' .. keyComboDesc .. '\' is failed')
end
end
function Hotkeys.unbind(keyComboDesc, widget)
function Hotkeys.bindKeyPress(keyComboDesc, callback, widget)
widget = widget or rootWidget
if widget.boundKeyCombos == nil then return end
connectKeyPressEvent(widget)
local keyComboDesc = retranslateKeyComboDesc(keyComboDesc)
if keyComboDesc then
widget.boundKeyCombos[keyComboDesc] = nil
widget.boundKeyPressCombos[keyComboDesc] = callback
else
error('key combo \'' .. keyComboDesc .. '\' is failed')
end
end
function Hotkeys.unbindKeyDown(keyComboDesc, widget)
widget = widget or rootWidget
if widget.boundKeyDownCombos == nil then return end
local keyComboDesc = retranslateKeyComboDesc(keyComboDesc)
if keyComboDesc then
widget.boundKeyDownCombos[keyComboDesc] = nil
end
end

View File

@ -5,7 +5,7 @@ local displayedMenuList = {}
function UIPopupMenu.create()
local menu = UIPopupMenu.internalCreate()
local layout = UIVerticalLayout.create(menu)
layout:setFitParent(true)
layout:setFitChildren(true)
menu:setLayout(layout)
return menu
end
@ -53,7 +53,8 @@ function UIPopupMenu:onMousePress(mousePos, mouseButton)
return false
end
function UIPopupMenu:onKeyPress(keyCode, keyText, keyboardModifiers)
function UIPopupMenu:onKeyPress(keyCode, keyboardModifiers, wouldFilter)
if wouldFilter then return end
if keyCode == KeyEscape then
self:destroy()
return true

View File

@ -6,7 +6,8 @@ function UIWindow.create()
return window
end
function UIWindow:onKeyPress(keyCode, keyText, keyboardModifiers)
function UIWindow:onKeyPress(keyCode, keyboardModifiers, wouldFilter)
if wouldFilter then return end
if keyboardModifiers == KeyboardNoModifier then
if keyCode == KeyReturn or keyCode == KeyEnter then
signalcall(self.onEnter, self)

View File

@ -1,5 +1,6 @@
-- private functions
local function onGameKeyPress(self, keyCode, keyText, keyboardModifiers)
local function onGameKeyPress(self, keyCode, keyboardModifiers, wouldFilter)
if wouldFilter then return end
if keyboardModifiers == KeyboardCtrlModifier then
if keyCode == KeyG then
CharacterList.show()
@ -17,6 +18,17 @@ function Game.createInterface()
Background.hide()
CharacterList.destroyLoadBox()
Game.gameUi = displayUI('game.otui')
--Hotkeys.bindKeyPress('Up', function() Game.walk(North) end)
--Hotkeys.bindKeyPress('Down', function() Game.walk(South) end)
--Hotkeys.bindKeyPress('Left', function() Game.walk(West) end)
--Hotkeys.bindKeyPress('Right', function() Game.walk(East) end)
Hotkeys.bindKeyPress('Ctrl+Shift+Up', function() Game.forceWalk(North) end)
Hotkeys.bindKeyPress('Ctrl+Shift+Down', function() Game.forceWalk(South) end)
Hotkeys.bindKeyPress('Ctrl+Shift+Left', function() Game.forceWalk(West) end)
Hotkeys.bindKeyPress('Ctrl+Shift+Right', function() Game.forceWalk(East) end)
rootWidget:moveChildToIndex(Game.gameUi, 1)
Game.gameMapPanel = Game.gameUi:getChildById('gameMapPanel')
Game.gameRightPanel = Game.gameUi:getChildById('gameRightPanel')

View File

@ -48,10 +48,10 @@ function Console.create()
Console.addChannel('Default', 0)
Console.addTab('Server Log')
Hotkeys.bind('Tab', function() consoleTabBar:selectNextTab() end, consolePanel)
Hotkeys.bind('Shift+Tab', function() consoleTabBar:selectPrevTab() end, consolePanel)
Hotkeys.bind('Enter', Console.sendCurrentMessage, consolePanel)
Hotkeys.bind('Return', Console.sendCurrentMessage, consolePanel)
Hotkeys.bindKeyDown('Tab', function() consoleTabBar:selectNextTab() end, consolePanel)
Hotkeys.bindKeyDown('Shift+Tab', function() consoleTabBar:selectPrevTab() end, consolePanel)
Hotkeys.bindKeyDown('Enter', Console.sendCurrentMessage, consolePanel)
Hotkeys.bindKeyDown('Return', Console.sendCurrentMessage, consolePanel)
end
function Console.destroy()

View File

@ -1,18 +1,18 @@
-- this file use loaded after everything is loaded and initialized
-- you can place any custom user code here
Hotkeys.bind('F1', function() Game.talk('exura gran') end)
Hotkeys.bind('F2', function() Game.talk('exori mort') end)
Hotkeys.bind('F3', function() Game.talk('exori frigo') end)
Hotkeys.bind('F4', function() Game.talk('exevo vis hur') end)
Hotkeys.bind('F5', function() Game.talk('utani gran hur') end)
Hotkeys.bind('F6', function() Game.talk('exani tera') end)
Hotkeys.bindKeyDown('F1', function() Game.talk('exura gran') end)
Hotkeys.bindKeyDown('F2', function() Game.talk('exori mort') end)
Hotkeys.bindKeyDown('F3', function() Game.talk('exori frigo') end)
Hotkeys.bindKeyDown('F4', function() Game.talk('exevo vis hur') end)
Hotkeys.bindKeyDown('F5', function() Game.talk('utani gran hur') end)
Hotkeys.bindKeyDown('F6', function() Game.talk('exani tera') end)
local function reload()
runscript('otclientrc.lua')
TextMessage.displayEventAdvance('Script otclientrc.lua reloaded.')
print('Script otclient.rc lua reloaded')
end
Hotkeys.bind('Ctrl+R', reload)
Hotkeys.bindKeyDown('Ctrl+R', reload)
rcloaded = true

View File

@ -228,6 +228,8 @@ namespace Fw
enum InputEventType {
NoInputEvent = 0,
KeyTextInputEvent,
KeyDownInputEvent,
KeyPressInputEvent,
KeyReleaseInputEvent,
MousePressInputEvent,

View File

@ -76,10 +76,8 @@ void ConfigManager::setList(const std::string& key, const std::vector<std::strin
return;
OTMLNodePtr child = OTMLNode::create(key, true);
for(const std::string& value : list) {
for(const std::string& value : list)
child->writeIn(value);
dump << "insert" << value;
}
m_confsDoc->addChild(child);
}

View File

@ -26,14 +26,30 @@
#include "declarations.h"
struct InputEvent {
InputEvent() {
reset();
keyboardModifiers = 0;
}
void reset(Fw::InputEventType eventType = Fw::NoInputEvent) {
type = eventType;
wheelDirection = Fw::MouseNoWheel;
mouseButton = Fw::MouseNoButton;
keyCode = Fw::KeyUnknown;
keyText = "";
mouseMoved = Point();
wouldFilter = false;
};
Fw::InputEventType type;
Fw::MouseWheelDirection wheelDirection;
Fw::MouseButton mouseButton;
int keyboardModifiers;
std::string keyText;
Fw::Key keyCode;
std::string keyText;
int keyboardModifiers;
Point mousePos;
Point mouseMoved;
bool wouldFilter;
};
#endif

View File

@ -29,7 +29,7 @@ Particle::Particle(const Point& pos, const Size& startSize, const Size& finalSiz
m_colors = colors;
m_colorsStops = colorsStops;
m_position = PointF(pos.x, pos.y);
m_pos = 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_position + delta;
PointF position = m_pos + delta;
if(m_position != position) {
if(m_pos != position) {
mustRedraw = true;
m_position += delta;
m_pos += delta;
}
// update acceleration
m_velocity += m_acceleration * elapsedTime;
}
m_rect.move((int)m_position.x - m_size.width() / 2, (int)m_position.y - m_size.height() / 2);
m_rect.move((int)m_pos.x - m_size.width() / 2, (int)m_pos.y - m_size.height() / 2);
}
void Particle::updateSize()

View File

@ -36,10 +36,10 @@ public:
bool hasFinished() { return m_finished; }
PointF getPos() { return m_position; }
PointF getPos() { return m_pos; }
PointF getVelocity() { return m_velocity; }
void setPos(const PointF& position) { m_position = position; }
void setPos(const PointF& position) { m_pos = 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_position;
PointF m_pos;
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_position = childNode->value<Point>();
m_pos = childNode->value<Point>();
else if(childNode->tag() == "acceleration")
m_acceleration = childNode->value<float>();
else if(childNode->tag() == "velocity-reduction-percent")
@ -132,7 +132,7 @@ void AttractionAffector::updateParticle(const ParticlePtr& particle, double elap
return;
PointF pPosition = particle->getPos();
PointF d = PointF(m_position.x - pPosition.x, pPosition.y - m_position.y);
PointF d = PointF(m_pos.x - pPosition.x, pPosition.y - m_pos.y);
if(d.length() == 0)
return;

View File

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

View File

@ -31,7 +31,7 @@ ParticleEmitter::ParticleEmitter(const ParticleSystemPtr& parent)
{
m_parent = parent;
m_position = Point(0, 0);
m_pos = 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_position = childNode->value<Point>();
m_pos = 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_position + Point(pRadius * cos(pAngle), pRadius * sin(pAngle));
Point pPosition = m_pos + 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_position;
Point m_pos;
float m_duration, m_delay;
double m_elapsedTime;
bool m_finished, m_active;

View File

@ -294,12 +294,12 @@ void Application::registerLuaFunctions()
g_lua.bindClassMemberFunction<UIBoxLayout>("setFitChildren", &UIBoxLayout::setFitChildren);
// UIVerticalLayout
g_lua.registerClass<UIVerticalLayout, UILayout>();
g_lua.registerClass<UIVerticalLayout, UIBoxLayout>();
g_lua.bindClassStaticFunction<UIVerticalLayout>("create", [](UIWidgetPtr parent){ return UIVerticalLayoutPtr(new UIVerticalLayout(parent)); } );
g_lua.bindClassMemberFunction<UIVerticalLayout>("setAlignBottom", &UIVerticalLayout::setAlignBottom);
// UIHorizontalLayout
g_lua.registerClass<UIHorizontalLayout, UILayout>();
g_lua.registerClass<UIHorizontalLayout, UIBoxLayout>();
g_lua.bindClassStaticFunction<UIHorizontalLayout>("create", [](UIWidgetPtr parent){ return UIHorizontalLayoutPtr(new UIHorizontalLayout(parent)); } );
g_lua.bindClassMemberFunction<UIHorizontalLayout>("setAlignRight", &UIHorizontalLayout::setAlignRight);
@ -404,6 +404,7 @@ void Application::registerLuaFunctions()
g_lua.bindClassStaticFunction("g_window", "getY", std::bind(&PlatformWindow::getY, &g_window));
g_lua.bindClassStaticFunction("g_window", "getMousePos", std::bind(&PlatformWindow::getMousePos, &g_window));
g_lua.bindClassStaticFunction("g_window", "getKeyboardModifiers", std::bind(&PlatformWindow::getKeyboardModifiers, &g_window));
g_lua.bindClassStaticFunction("g_window", "isKeyPressed", std::bind(&PlatformWindow::isKeyPressed, &g_window, _1));
g_lua.bindClassStaticFunction("g_window", "isVisible", std::bind(&PlatformWindow::isVisible, &g_window));
g_lua.bindClassStaticFunction("g_window", "isFullscreen", std::bind(&PlatformWindow::isFullscreen, &g_window));
g_lua.bindClassStaticFunction("g_window", "isMaximized", std::bind(&PlatformWindow::isMaximized, &g_window));

View File

@ -27,6 +27,7 @@
WIN32Window window;
#else
#include "x11window.h"
#include <framework/core/clock.h>
X11Window window;
#endif
@ -39,3 +40,83 @@ void PlatformWindow::updateUnmaximizedCoords()
m_unmaximizedSize = m_size;
}
}
void PlatformWindow::processKeyDown(Fw::Key keyCode)
{
if(keyCode == Fw::KeyUnknown || m_keysState[keyCode])
return;
m_keysState[keyCode] = true;
m_lastKeysPress[keyCode] = -1;
if(keyCode == Fw::KeyCtrl)
m_inputEvent.keyboardModifiers |= Fw::KeyboardCtrlModifier;
else if(keyCode == Fw::KeyAlt)
m_inputEvent.keyboardModifiers |= Fw::KeyboardAltModifier;
else if(keyCode == Fw::KeyShift)
m_inputEvent.keyboardModifiers |= Fw::KeyboardShiftModifier;
m_inputEvent.reset();
m_inputEvent.type = Fw::KeyDownInputEvent;
m_inputEvent.keyCode = keyCode;
if(m_onInputEvent) {
m_onInputEvent(m_inputEvent);
m_inputEvent.reset(Fw::KeyPressInputEvent);
m_inputEvent.keyCode = keyCode;
m_lastKeysPress[keyCode] = g_clock.ticks();
m_firstKeysPress[keyCode] = g_clock.ticks();
m_onInputEvent(m_inputEvent);
}
}
void PlatformWindow::processKeyRelease(Fw::Key keyCode)
{
if(keyCode == Fw::KeyUnknown || !m_keysState[keyCode])
return;
m_keysState[keyCode] = false;
if(keyCode == Fw::KeyCtrl)
m_inputEvent.keyboardModifiers &= ~Fw::KeyboardCtrlModifier;
else if(keyCode == Fw::KeyAlt)
m_inputEvent.keyboardModifiers &= ~Fw::KeyboardAltModifier;
else if(keyCode == Fw::KeyShift)
m_inputEvent.keyboardModifiers &= ~Fw::KeyboardShiftModifier;
if(m_onInputEvent) {
m_inputEvent.reset(Fw::KeyReleaseInputEvent);
m_onInputEvent(m_inputEvent);
}
}
void PlatformWindow::fireKeysPress()
{
// avoid massive checks
if(m_keyPressTimer.ticksElapsed() < 10)
return;
m_keyPressTimer.restart();
for(auto it : m_keysState) {
Fw::Key keyCode = it.first;
bool pressed = it.second;
if(!pressed)
continue;
ticks_t lastPressTicks = m_lastKeysPress[keyCode];
ticks_t firstKeyPress = m_firstKeysPress[keyCode];
if(g_clock.ticksElapsed(lastPressTicks) >= KEY_PRESS_REPEAT_INTERVAL) {
if(m_onInputEvent) {
m_inputEvent.reset();
m_inputEvent.type = Fw::KeyPressInputEvent;
m_inputEvent.keyCode = keyCode;
m_inputEvent.wouldFilter = g_clock.ticksElapsed(firstKeyPress) < KEY_PRESS_REPEAT_DELAY;
m_onInputEvent(m_inputEvent);
}
m_lastKeysPress[keyCode] = g_clock.ticks();
}
}
}

View File

@ -25,9 +25,15 @@
#include <framework/global.h>
#include <framework/core/inputevent.h>
#include <framework/core/timer.h>
class PlatformWindow
{
enum {
KEY_PRESS_REPEAT_INTERVAL = 30,
KEY_PRESS_REPEAT_DELAY = 500
};
typedef std::function<void(const Size&)> OnResizeCallback;
typedef std::function<void(const InputEvent&)> OnInputEventCallback;
@ -72,6 +78,7 @@ public:
int getY() { return m_pos.y; }
Point getMousePos() { return m_inputEvent.mousePos; }
int getKeyboardModifiers() { return m_inputEvent.keyboardModifiers; }
bool isKeyPressed(Fw::Key keyCode) { return m_keysState[keyCode]; }
bool isVisible() { return m_visible; }
bool isFullscreen() { return m_fullscreen; }
@ -85,6 +92,16 @@ public:
protected:
void updateUnmaximizedCoords();
void processKeyDown(Fw::Key keyCode);
void processKeyRelease(Fw::Key keyCode);
void fireKeysPress();
std::map<int, Fw::Key> m_keyMap;
std::map<Fw::Key, Boolean<false>> m_keysState;
std::map<Fw::Key, ticks_t> m_firstKeysPress;
std::map<Fw::Key, ticks_t> m_lastKeysPress;
Timer m_keyPressTimer;
Size m_size;
Point m_pos;
Size m_unmaximizedSize;

View File

@ -35,7 +35,6 @@ WIN32Window::WIN32Window()
m_maximized = false;
m_minimumSize = Size(16,16);
m_size = m_minimumSize;
m_inputEvent.keyboardModifiers = 0;
m_keyMap[VK_ESCAPE] = Fw::KeyEscape;
m_keyMap[VK_TAB] = Fw::KeyTab;

View File

@ -82,7 +82,6 @@ private:
HGLRC m_glContext;
bool m_maximized;
Size m_minimumSize;
std::map<int, Fw::Key> m_keyMap;
};
#endif

View File

@ -39,7 +39,6 @@ X11Window::X11Window()
m_screen = 0;
m_wmDelete = 0;
m_size = Size(16,16);
m_inputEvent.keyboardModifiers = 0;
#ifndef OPENGL_ES2
m_glxContext = 0;
@ -526,33 +525,41 @@ void X11Window::poll()
while(XPending(m_display) > 0) {
XNextEvent(m_display, &event);
// check for repeated key releases
bool repatedKeyRelease = false;
if(event.type == KeyRelease && XPending(m_display)) {
XPeekEvent(m_display, &peekEvent);
if((peekEvent.type == KeyPress) && (peekEvent.xkey.keycode == event.xkey.keycode) && ((peekEvent.xkey.time-event.xkey.time) < 2))
repatedKeyRelease = true;
}
// process keydown and keyrelease events first
if(event.type == KeyPress || (event.type == KeyRelease && !repatedKeyRelease)) {
// remove caps lock and shift maks
XKeyEvent xkey = event.xkey;
xkey.state &= ~(ShiftMask | LockMask);
// lookup keysym and translate it
KeySym keysym;
char buf[32];
int len = XLookupString(&xkey, buf, sizeof(buf), &keysym, 0);
Fw::Key keyCode = Fw::KeyUnknown;
if(m_keyMap.find(keysym) != m_keyMap.end())
keyCode = m_keyMap[keysym];
if(event.type == KeyPress)
processKeyDown(keyCode);
else if(event.type == KeyRelease)
processKeyRelease(keyCode);
}
// call filter because xim will discard KeyPress events when keys still composing
if(XFilterEvent(&event, m_window))
continue;
// discard events of repeated key releases
if(event.type == KeyRelease && XPending(m_display)) {
XPeekEvent(m_display, &peekEvent);
if((peekEvent.type == KeyPress) &&
(peekEvent.xkey.keycode == event.xkey.keycode) &&
((peekEvent.xkey.time-event.xkey.time) < 2))
// discard repated key releases
if(repatedKeyRelease)
continue;
}
// reset inputEvent values, except keyboardModifiers and mousePos
m_inputEvent.type = Fw::NoInputEvent;
m_inputEvent.mouseButton = Fw::MouseNoButton;
m_inputEvent.keyCode = Fw::KeyUnknown;
m_inputEvent.keyText = "";
m_inputEvent.mouseMoved = Point();
m_inputEvent.wheelDirection = Fw::MouseNoWheel;
m_inputEvent.keyboardModifiers = 0;
if(event.xkey.state & ControlMask)
m_inputEvent.keyboardModifiers |= Fw::KeyboardCtrlModifier;
if(event.xkey.state & ShiftMask)
m_inputEvent.keyboardModifiers |= Fw::KeyboardShiftModifier;
if(event.xkey.state & Mod1Mask)
m_inputEvent.keyboardModifiers |= Fw::KeyboardAltModifier;
switch(event.type) {
case ClientMessage: {
@ -614,15 +621,19 @@ void X11Window::poll()
XFlush(m_display);
break;
}
case KeyPress:
case KeyRelease: {
// process text events
case KeyPress: {
// text cant be insert while holding ctrl or alt
if(event.xkey.state & ControlMask || event.xkey.state & Mod1Mask)
break;
// process key text events
KeySym keysym;
char buf[32];
memset(buf, 0, 32);
int len;
// lookup for keyText
if(event.type == KeyPress && !(event.xkey.state & ControlMask) && !(event.xkey.state & Mod1Mask)) {
if(m_xic) { // with xim we can get latin1 input correctly
Status status;
len = XmbLookupString(m_xic, &event.xkey, buf, sizeof(buf), &keysym, &status);
@ -631,38 +642,23 @@ void X11Window::poll()
len = XLookupString(&event.xkey, buf, sizeof(buf), &keysym, &compose);
}
if(len > 0 &&
// these keys produces characters that we don't want to capture
keysym != XK_BackSpace &&
keysym != XK_Return &&
keysym != XK_Delete &&
keysym != XK_Escape &&
(uchar)(buf[0]) >= 32
) {
//logDebug("char: ", buf[0], " code: ", (uint)buf[0]);
m_inputEvent.keyText = buf;
}
}
// filter unwanted characters
if(len == 0 || (uchar)(buf[0]) < 32 || keysym == XK_BackSpace || keysym == XK_Return || keysym == XK_Delete || keysym == XK_Escape)
break;
std::string text = buf;
XKeyEvent xkey = event.xkey;
xkey.state = xkey.state & ~(ShiftMask);
len = XLookupString(&xkey, buf, sizeof(buf), &keysym, 0);
if(len > 0 && m_inputEvent.keyText.length() == 0 && keysym != XK_BackSpace &&
keysym != XK_Return &&
keysym != XK_Delete &&
keysym != XK_Escape)
m_inputEvent.keyText = buf;
//logDebug("char: ", buf[0], " code: ", (int)((uchar)buf[0]));
if(m_keyMap.find(keysym) != m_keyMap.end())
m_inputEvent.keyCode = m_keyMap[keysym];
m_inputEvent.type = (event.type == KeyPress) ? Fw::KeyPressInputEvent : Fw::KeyReleaseInputEvent;
if(m_inputEvent.keyCode != Fw::KeyUnknown || !m_inputEvent.keyText.empty())
if(m_onInputEvent && text.length() > 0) {
m_inputEvent.reset(Fw::KeyTextInputEvent);
m_inputEvent.keyText = text;
m_onInputEvent(m_inputEvent);
}
break;
}
case ButtonPress:
case ButtonRelease: {
m_inputEvent.reset();
m_inputEvent.type = (event.type == ButtonPress) ? Fw::MousePressInputEvent : Fw::MouseReleaseInputEvent;
switch(event.xbutton.button) {
case Button1:
@ -694,6 +690,7 @@ void X11Window::poll()
}
case MotionNotify: {
m_inputEvent.reset();
m_inputEvent.type = Fw::MouseMoveInputEvent;
Point newMousePos(event.xbutton.x, event.xbutton.y);
m_inputEvent.mouseMoved = newMousePos - m_inputEvent.mousePos;
@ -722,6 +719,8 @@ void X11Window::poll()
if(needsResizeUpdate && m_onResize)
m_onResize(m_size);
fireKeysPress();
}
void X11Window::swapBuffers()

View File

@ -93,7 +93,6 @@ private:
int m_screen;
Atom m_wmDelete;
std::string m_clipboardText;
std::map<int, Fw::Key> m_keyMap;
#ifndef OPENGL_ES2
GLXContext m_glxContext;

View File

@ -389,6 +389,8 @@ void UILineEdit::onStyleApply(const std::string& styleName, const OTMLNodePtr& s
setTextHorizontalMargin(node->value<int>());
else if(node->tag() == "always-active")
setAlwaysActive(node->value<bool>());
//else if(node->tag() == "disable-arrow-navitation")
// setArrowNavigation(node->value<bool>());
}
}
@ -409,11 +411,12 @@ void UILineEdit::onFocusChange(bool focused, Fw::FocusReason reason)
UIWidget::onFocusChange(focused, reason);
}
bool UILineEdit::onKeyPress(uchar keyCode, std::string keyText, int keyboardModifiers)
bool UILineEdit::onKeyPress(uchar keyCode, int keyboardModifiers, bool wouldFilter)
{
if(UIWidget::onKeyPress(keyCode, keyText, keyboardModifiers))
if(UIWidget::onKeyPress(keyCode, keyboardModifiers, wouldFilter))
return true;
if(!wouldFilter) {
if(keyCode == Fw::KeyDelete) // erase right character
removeCharacter(true);
else if(keyCode == Fw::KeyBackspace) // erase left character {
@ -433,12 +436,18 @@ bool UILineEdit::onKeyPress(uchar keyCode, std::string keyText, int keyboardModi
if(UIWidgetPtr parent = getParent())
parent->focusNextChild(Fw::TabFocusReason);
}
} else if(!keyText.empty() && (keyboardModifiers == Fw::KeyboardNoModifier || keyboardModifiers == Fw::KeyboardShiftModifier))
appendText(keyText);
else
} else
return false;
return true;
}
return false;
}
bool UILineEdit::onKeyText(const std::string& keyText)
{
appendText(keyText);
return true;
}
bool UILineEdit::onMousePress(const Point& mousePos, Fw::MouseButton button)
{

View File

@ -61,7 +61,8 @@ protected:
virtual void onStyleApply(const std::string& styleName, const OTMLNodePtr& styleNode);
virtual void onGeometryChange(const Rect& oldRect, const Rect& newRect);
virtual void onFocusChange(bool focused, Fw::FocusReason reason);
virtual bool onKeyPress(uchar keyCode, std::string keyText, int keyboardModifiers);
virtual bool onKeyText(const std::string& keyText);
virtual bool onKeyPress(uchar keyCode, int keyboardModifiers, bool wouldFilter);
virtual bool onMousePress(const Point& mousePos, Fw::MouseButton button);
private:

View File

@ -59,11 +59,17 @@ void UIManager::inputEvent(const InputEvent& event)
{
m_isOnInputEvent = true;
switch(event.type) {
case Fw::KeyTextInputEvent:
m_keyboardReceiver->propagateOnKeyText(event.keyText);
break;
case Fw::KeyDownInputEvent:
m_keyboardReceiver->propagateOnKeyDown(event.keyCode, event.keyboardModifiers);
break;
case Fw::KeyPressInputEvent:
m_keyboardReceiver->propagateOnKeyPress(event.keyCode, event.keyText, event.keyboardModifiers);
m_keyboardReceiver->propagateOnKeyPress(event.keyCode, event.keyboardModifiers, event.wouldFilter);
break;
case Fw::KeyReleaseInputEvent:
m_keyboardReceiver->propagateOnKeyRelease(event.keyCode, event.keyText, event.keyboardModifiers);
m_keyboardReceiver->propagateOnKeyRelease(event.keyCode, event.keyboardModifiers);
break;
case Fw::MousePressInputEvent:
m_keyboardReceiver->propagateOnMousePress(event.mousePos, event.mouseButton);

View File

@ -1053,14 +1053,24 @@ void UIWidget::onHoverChange(bool hovered)
g_ui.getRootWidget()->updateState(Fw::HoverState);
}
bool UIWidget::onKeyPress(uchar keyCode, std::string keyText, int keyboardModifiers)
bool UIWidget::onKeyText(const std::string& keyText)
{
return callLuaField<bool>("onKeyPress", keyCode, keyText, keyboardModifiers);
return callLuaField<bool>("onKeyText", keyText);
}
bool UIWidget::onKeyRelease(uchar keyCode, std::string keyText, int keyboardModifiers)
bool UIWidget::onKeyDown(uchar keyCode, int keyboardModifiers)
{
return callLuaField<bool>("onKeyRelease", keyCode, keyText, keyboardModifiers);
return callLuaField<bool>("onKeyDown", keyCode, keyboardModifiers);
}
bool UIWidget::onKeyPress(uchar keyCode, int keyboardModifiers, bool wouldFilter)
{
return callLuaField<bool>("onKeyPress", keyCode, keyboardModifiers, wouldFilter);
}
bool UIWidget::onKeyRelease(uchar keyCode, int keyboardModifiers)
{
return callLuaField<bool>("onKeyRelease", keyCode, keyboardModifiers);
}
bool UIWidget::onMousePress(const Point& mousePos, Fw::MouseButton button)
@ -1086,7 +1096,7 @@ bool UIWidget::onMouseWheel(const Point& mousePos, Fw::MouseWheelDirection direc
return callLuaField<bool>("onMouseWheel", mousePos, direction);
}
bool UIWidget::propagateOnKeyPress(uchar keyCode, std::string keyText, int keyboardModifiers)
bool UIWidget::propagateOnKeyText(const std::string& keyText)
{
// do a backup of children list, because it may change while looping it
UIWidgetList children;
@ -1101,14 +1111,58 @@ bool UIWidget::propagateOnKeyPress(uchar keyCode, std::string keyText, int keybo
}
for(const UIWidgetPtr& child : children) {
if(child->propagateOnKeyPress(keyCode, keyText, keyboardModifiers))
if(child->propagateOnKeyText(keyText))
return true;
}
return onKeyPress(keyCode, keyText, keyboardModifiers);
return onKeyText(keyText);
}
bool UIWidget::propagateOnKeyRelease(uchar keyCode, std::string keyText, int keyboardModifiers)
bool UIWidget::propagateOnKeyDown(uchar keyCode, int keyboardModifiers)
{
// do a backup of children list, because it may change while looping it
UIWidgetList children;
for(const UIWidgetPtr& child : m_children) {
// events on hidden or disabled widgets are discarded
if(!child->isExplicitlyEnabled() || !child->isExplicitlyVisible())
continue;
// key events go only to containers or focused child
if(child->isFocused())
children.push_back(child);
}
for(const UIWidgetPtr& child : children) {
if(child->propagateOnKeyDown(keyCode, keyboardModifiers))
return true;
}
return onKeyDown(keyCode, keyboardModifiers);
}
bool UIWidget::propagateOnKeyPress(uchar keyCode, int keyboardModifiers, bool wouldFilter)
{
// do a backup of children list, because it may change while looping it
UIWidgetList children;
for(const UIWidgetPtr& child : m_children) {
// events on hidden or disabled widgets are discarded
if(!child->isExplicitlyEnabled() || !child->isExplicitlyVisible())
continue;
// key events go only to containers or focused child
if(child->isFocused())
children.push_back(child);
}
for(const UIWidgetPtr& child : children) {
if(child->propagateOnKeyPress(keyCode, keyboardModifiers, wouldFilter))
return true;
}
return onKeyPress(keyCode, keyboardModifiers, wouldFilter);
}
bool UIWidget::propagateOnKeyRelease(uchar keyCode, int keyboardModifiers)
{
// do a backup of children list, because it may change while looping it
UIWidgetList children;
@ -1123,11 +1177,11 @@ bool UIWidget::propagateOnKeyRelease(uchar keyCode, std::string keyText, int key
}
for(const UIWidgetPtr& child : children) {
if(child->propagateOnKeyRelease(keyCode, keyText, keyboardModifiers))
if(child->propagateOnKeyRelease(keyCode, keyboardModifiers))
return true;
}
return onKeyRelease(keyCode, keyText, keyboardModifiers);
return onKeyRelease(keyCode, keyboardModifiers);
}
bool UIWidget::propagateOnMousePress(const Point& mousePos, Fw::MouseButton button)

View File

@ -161,15 +161,19 @@ protected:
virtual void onGeometryChange(const Rect& oldRect, const Rect& newRect);
virtual void onFocusChange(bool focused, Fw::FocusReason reason);
virtual void onHoverChange(bool hovered);
virtual bool onKeyPress(uchar keyCode, std::string keyText, int keyboardModifiers);
virtual bool onKeyRelease(uchar keyCode, std::string keyText, int keyboardModifiers);
virtual bool onKeyText(const std::string& keyText);
virtual bool onKeyDown(uchar keyCode, int keyboardModifiers);
virtual bool onKeyPress(uchar keyCode, int keyboardModifiers, bool wouldFilter);
virtual bool onKeyRelease(uchar keyCode, int keyboardModifiers);
virtual bool onMousePress(const Point& mousePos, Fw::MouseButton button);
virtual void onMouseRelease(const Point& mousePos, Fw::MouseButton button);
virtual bool onMouseMove(const Point& mousePos, const Point& mouseMoved);
virtual bool onMouseWheel(const Point& mousePos, Fw::MouseWheelDirection direction);
bool propagateOnKeyPress(uchar keyCode, std::string keyText, int keyboardModifiers);
bool propagateOnKeyRelease(uchar keyCode, std::string keyText, int keyboardModifiers);
bool propagateOnKeyText(const std::string& keyText);
bool propagateOnKeyDown(uchar keyCode, int keyboardModifiers);
bool propagateOnKeyPress(uchar keyCode, int keyboardModifiers, bool wouldFilter);
bool propagateOnKeyRelease(uchar keyCode, int keyboardModifiers);
bool propagateOnMousePress(const Point& mousePos, Fw::MouseButton button);
void propagateOnMouseRelease(const Point& mousePos, Fw::MouseButton button);
bool propagateOnMouseMove(const Point& mousePos, const Point& mouseMoved);

View File

@ -47,7 +47,7 @@ Creature::Creature() : Thing()
m_walkTimePerPixel = 1000.0/32.0;
m_walking = false;
m_inverseWalking = true;
m_preWalking = false;
m_skull = Otc::SkullNone;
m_shield = Otc::ShieldNone;
@ -189,52 +189,45 @@ void Creature::drawInformation(int x, int y, bool useGray, const Rect& visibleRe
}
}
void Creature::walk(const Position& position, bool inverse)
void Creature::walk(const Position& oldPos, const Position& newPos, bool preWalk)
{
// We're walking
if(m_position.isInRange(position, 1, 1, 0)) {
Otc::Direction direction = m_position.getDirectionFromPosition(position);
// 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;
}
setDirection(direction);
if(inverse) {
Position positionDelta = m_position - position;
m_walkOffset = Point(positionDelta.x * Map::NUM_TILE_PIXELS, positionDelta.y * Map::NUM_TILE_PIXELS);
}
else
m_walkOffset = Point(0, 0);
// Diagonal walking lasts 3 times more.
// diagonal walking lasts 3 times more.
int walkTimeFactor = 1;
if(direction == Otc::NorthWest || direction == Otc::NorthEast || direction == Otc::SouthWest || direction == Otc::SouthEast)
walkTimeFactor = 3;
// Get walking speed
int groundSpeed = 100;
if(ItemPtr ground = g_map.getTile(position)->getGround())
groundSpeed = ground->getType()->parameters[ThingType::GroundSpeed];
// 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();
float walkTime = 1000.0 * (float)groundSpeed / m_speed;
walkTime = (walkTime == 0) ? 1000 : walkTime;
walkTime = std::ceil(walkTime / g_game.getServerBeat()) * g_game.getServerBeat();
bool sameWalk = m_walking && !m_inverseWalking && inverse;
m_inverseWalking = inverse;
m_walkTimePerPixel = walkInterval / 32.0;
m_walkOffset = Point();
m_walkStart = g_clock.ticks();
m_walkEnd = m_walkStart + walkInterval * walkTimeFactor;
m_walking = true;
m_walkTimePerPixel = walkTime / 32.0;
m_walkStart = sameWalk ? m_walkStart : g_clock.ticks();
m_walkEnd = m_walkStart + walkTime * walkTimeFactor;
m_preWalking = preWalk;
m_turnDirection = m_direction;
updateWalk();
}
// Teleport
else {
m_walking = false;
m_walkOffset = Point(0, 0);
m_animation = 0;
}
}
void Creature::turn(Otc::Direction direction)
{
@ -252,7 +245,7 @@ void Creature::updateWalk()
int elapsedTicks = g_clock.ticksElapsed(m_walkStart);
int totalPixelsWalked = std::min((int)round(elapsedTicks / m_walkTimePerPixel), 32);
if(m_inverseWalking) {
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)
@ -262,8 +255,7 @@ void Creature::updateWalk()
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 {
} 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)
@ -282,20 +274,26 @@ void Creature::updateWalk()
m_animation = 1 + totalPixelsWalked * 4 / Map::NUM_TILE_PIXELS % (m_type->dimensions[ThingType::AnimationPhases] - 1);
}
if(g_clock.ticks() > m_walkEnd)
if(g_clock.ticks() > m_walkEnd) {
cancelWalk(m_turnDirection);
else
} else
g_dispatcher.scheduleEvent(std::bind(&Creature::updateWalk, asCreature()), m_walkTimePerPixel);
}
void Creature::cancelWalk(Otc::Direction direction, bool)
void Creature::cancelWalk(Otc::Direction direction, bool force)
{
if(force) {
m_walkOffset = Point();
m_preWalking = false;
} else if(!m_preWalking)
m_walkOffset = Point();
m_walking = false;
m_walkStart = 0;
if(direction != Otc::InvalidDirection)
setDirection(direction);
if(m_outfit.getCategory() == ThingsType::Creature)
m_animation = 0;
m_walkOffset = Point(0, 0);
setDirection(direction);
}
void Creature::setName(const std::string& name)
@ -345,8 +343,7 @@ void Creature::setDirection(Otc::Direction direction)
m_xPattern = Otc::West;
else
m_xPattern = direction;
}
else {
} else {
m_xPattern = 0;
}
@ -358,7 +355,7 @@ void Creature::setOutfit(const Outfit& outfit)
if(m_outfit.getCategory() != ThingsType::Effect && outfit.getCategory() == ThingsType::Effect) {
auto self = asCreature();
g_dispatcher.scheduleEvent([self]() {
self->updateAnimation();
self->updateInvisibleAnimation();
}, INVISIBLE_TICKS);
m_xPattern = 0;
@ -433,7 +430,7 @@ void Creature::addVolatileSquare(uint8 color)
}, VOLATILE_SQUARE_DURATION);
}
void Creature::updateAnimation()
void Creature::updateInvisibleAnimation()
{
if(m_animation == 1)
m_animation = 2;
@ -447,7 +444,7 @@ void Creature::updateAnimation()
if(g_game.isOnline() && m_outfit.getCategory() == ThingsType::Effect) {
auto self = asCreature();
g_dispatcher.scheduleEvent([self]() {
self->updateAnimation();
self->updateInvisibleAnimation();
}, INVISIBLE_TICKS);
}
}
@ -470,3 +467,4 @@ ThingType *Creature::getType()
{
return g_thingsType.getThingType(m_outfit.getId(), m_outfit.getCategory());
}

View File

@ -73,18 +73,19 @@ public:
uint8 getEmblem() { return m_emblem; }
bool getPassable() { return m_passable; }
void updateAnimation();
void updateInvisibleAnimation();
void updateShield();
ThingType *getType();
//virtual void walk(const Position& oldPos, const Position& newPos, bool inverse = true);
virtual void walk(const Position& position, bool inverse = true);
// walk related
void walk(const Position& oldPos, const Position& newPos, bool preWalk = false);
void turn(Otc::Direction direction);
virtual void cancelWalk(Otc::Direction direction, bool force = false);
void cancelWalk(Otc::Direction direction = Otc::InvalidDirection, bool force = false);
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()); }
@ -109,7 +110,7 @@ protected:
Color m_informationColor;
ticks_t m_walkStart, m_walkEnd;
bool m_walking, m_inverseWalking;
bool m_walking, m_preWalking;
float m_walkTimePerPixel;
Point m_walkOffset;
Otc::Direction m_turnDirection;

View File

@ -140,23 +140,22 @@ void Game::processInventoryChange(int slot, const ItemPtr& item)
void Game::processCreatureMove(const CreaturePtr& creature, const Position& oldPos, const Position& newPos)
{
/*
// walk
if(oldPos.isInRange(newPos, 1, 1, 0)) {
Otc::Direction direction = oldPos.getDirectionFromPosition(newPos);
creature->setDirection(direction);
creature->walk(oldPos, newPos);
// teleport
} else {
// stop animation on teleport
// stop walking on teleport
if(creature->isWalking())
creature->cancelWalk();
}
*/
if(!m_walkFeedback && creature == m_localPlayer) {
if(creature == m_localPlayer) {
if(!m_walkFeedback) {
updateWalkPing();
m_walkFeedback = true;
}
creature->walk(newPos);
}
}
void Game::processAttackCancel()
@ -176,18 +175,23 @@ void Game::processWalkCancel(Otc::Direction direction)
void Game::walk(Otc::Direction direction)
{
if(m_localPlayer->isFollowing()) {
cancelFollow();
if(!isOnline() || isDead() || !checkBotProtection())
return;
if(m_localPlayer->isFollowing())
cancelFollow();
if(!m_localPlayer->canWalk(direction))
return;
m_localPlayer->preWalk(direction);
forceWalk(direction);
}
if(!isOnline() || isDead() || !checkBotProtection() || !m_localPlayer->canWalk(direction))
return;
m_localPlayer->clientWalk(direction);
// ping calculation restarts when the local players try to walk one tile
void Game::forceWalk(Otc::Direction direction)
{
m_walkPingTimer.restart();
m_walkFeedback = false;
switch(direction) {
case Otc::North:
@ -215,8 +219,6 @@ void Game::walk(Otc::Direction direction)
m_protocolGame->sendWalkNorthWest();
break;
}
m_walkFeedback = false;
}
void Game::turn(Otc::Direction direction)
@ -334,6 +336,7 @@ void Game::rotate(const ThingPtr& thing)
m_protocolGame->sendRotateItem(thing->getPos(), thing->getId(), stackpos);
}
//TODO: move this to Thing class
int Game::getThingStackpos(const ThingPtr& thing)
{
// thing is at map

View File

@ -56,6 +56,7 @@ public:
// walk related
void walk(Otc::Direction direction);
void forceWalk(Otc::Direction direction);
void turn(Otc::Direction direction);
// item related

View File

@ -25,79 +25,20 @@
#include "game.h"
#include "tile.h"
LocalPlayer::LocalPlayer()
void LocalPlayer::preWalk(Otc::Direction direction)
{
m_clientWalking = false;
m_nextWalkDirection = Otc::InvalidDirection;
}
void LocalPlayer::clientWalk(Otc::Direction direction)
{
// We're not walking, so start a client walk.
assert(!m_walking);
Position newPos = m_position + Position::getPosFromDirection(direction);
Creature::walk(newPos, false);
m_clientWalking = true;
}
void LocalPlayer::walk(const Position& position, bool inverse)
{
// This can only be received by protocol, so its always inverse.
// If we're already walking, just finish it.
if(m_clientWalking) {
m_clientWalking = false;
Position pos = Position::getPosFromDirection(m_direction);
Point walkOffset = Point(m_walkOffset.x - pos.x * 32,
m_walkOffset.y - pos.y * 32);
Creature::walk(position, inverse);
// Restore walk offset, because we were already walking.
m_walkOffset = walkOffset;
}
// If we're not client walking, we'll just walk like every NPC. Ie: When player is pushed.
else
Creature::walk(position, inverse);
}
void LocalPlayer::cancelWalk(Otc::Direction direction, bool force)
{
// Server said we cant walk. Ie: houses, vip areas.
if(force) {
m_clientWalking = false;
Creature::cancelWalk(direction);
}
else {
// Walk finished, and we already received the confirmation from server.
if(m_walking && !m_clientWalking) {
m_clientWalking = false;
Creature::cancelWalk(direction);
if(m_nextWalkDirection != Otc::InvalidDirection) {
g_game.walk(m_nextWalkDirection);
m_nextWalkDirection = Otc::InvalidDirection;
}
}
//else..
// Walk finished, however we havent received the confirmation from server. So wait for it.
}
// we're not walking, so start a client walk.
Position newPos = m_pos + Position::getPosFromDirection(direction);
walk(m_pos, newPos, true);
}
bool LocalPlayer::canWalk(Otc::Direction direction)
{
if(m_walking) {
if(direction != m_direction && m_nextWalkDirection != direction)
m_nextWalkDirection = direction;
else if(direction == m_direction && m_nextWalkDirection != Otc::InvalidDirection)
m_nextWalkDirection = Otc::InvalidDirection;
if(m_walking || (m_preWalking && g_clock.ticksElapsed(m_walkEnd) < 1000))
return false;
}
Position newPos = m_position + Position::getPosFromDirection(direction);
TilePtr tile = g_map.getTile(newPos);
// check for blockable tiles in the walk direction
TilePtr tile = g_map.getTile(m_pos + Position::getPosFromDirection(direction));
if(!tile->isWalkable()) {
g_game.processTextMessage("statusSmall", "Sorry, not possible.");
return false;
@ -108,11 +49,13 @@ bool LocalPlayer::canWalk(Otc::Direction direction)
void LocalPlayer::setAttackingCreature(const CreaturePtr& creature)
{
// clear current attacking creature
if(m_attackingCreature) {
m_attackingCreature->hideStaticSquare();
m_attackingCreature = nullptr;
}
// set the new attacking creature
if(creature) {
creature->showStaticSquare(Fw::red);
m_attackingCreature = creature;
@ -121,11 +64,13 @@ void LocalPlayer::setAttackingCreature(const CreaturePtr& creature)
void LocalPlayer::setFollowingCreature(const CreaturePtr& creature)
{
// clear current following creature
if(m_followingCreature) {
m_followingCreature->hideStaticSquare();
m_followingCreature = nullptr;
}
// set the new attacking creature
if(creature) {
creature->showStaticSquare(Fw::green);
m_followingCreature = creature;

View File

@ -28,8 +28,6 @@
class LocalPlayer : public Player
{
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; }
@ -42,25 +40,21 @@ public:
double getStatistic(Otc::Statistic statistic) { return m_statistics[statistic]; }
CreaturePtr getAttackingCreature() { return m_attackingCreature; }
CreaturePtr getFollowingCreature() { return m_followingCreature; }
Otc::Direction getNextWalkDirection() { return m_nextWalkDirection; }
Otc::PlayerIcons getIcons() { return m_icons; }
bool isAttacking() { return m_attackingCreature != nullptr; }
bool isFollowing() { return m_followingCreature != nullptr; }
void clientWalk(Otc::Direction direction);
void walk(const Position& position, bool inverse);
void cancelWalk(Otc::Direction direction, bool force = false);
void preWalk(Otc::Direction direction);
bool canWalk(Otc::Direction direction);
LocalPlayerPtr asLocalPlayer() { return std::static_pointer_cast<LocalPlayer>(shared_from_this()); }
double getLevel() { return getStatistic(Otc::Level); }
//TODO: more gets
private:
bool m_canReportBugs;
bool m_clientWalking;
Otc::Direction m_nextWalkDirection;
CreaturePtr m_attackingCreature, m_followingCreature;
Otc::PlayerIcons m_icons;
int m_skills[Otc::LastSkill][Otc::LastSkillType];

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_positionDelta.x * time, m_positionDelta.y * time), 0);
internalDraw(p + Point(m_posDelta.x * time, m_posDelta.y * time), 0);
}
void Missile::setPath(const Position& fromPosition, const Position& toPosition)
@ -79,12 +79,12 @@ void Missile::setPath(const Position& fromPosition, const Position& toPosition)
m_yPattern = 1;
}
m_position = fromPosition;
m_positionDelta = toPosition - fromPosition;
m_pos = fromPosition;
m_posDelta = toPosition - fromPosition;
m_startTicks = g_clock.ticks();
m_duration = 150 * std::sqrt(Point(m_positionDelta.x, m_positionDelta.y).length());
m_positionDelta.x *= Map::NUM_TILE_PIXELS;
m_positionDelta.y *= Map::NUM_TILE_PIXELS;
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;
// schedule removal
auto self = asMissile();

View File

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

View File

@ -44,13 +44,14 @@ public:
virtual void draw(const Point& p, const Rect&) = 0;
void setId(uint32 id);
virtual void setPos(const Position& position) { m_position = position; }
virtual void setPos(const Position& position) { m_pos = position; }
uint32 getId() const { return m_id; }
Position getPos() const { return m_position; }
Position getPos() const { return m_pos; }
int getStackPriority();
virtual ThingType *getType();
int getAnimationPhases() { return m_type->dimensions[ThingType::AnimationPhases]; }
int getGroundSpeed() { return m_type->parameters[ThingType::GroundSpeed]; }
void setXPattern(int xPattern) { m_xPattern = xPattern; }
void setYPattern(int yPattern) { m_yPattern = yPattern; }
@ -87,7 +88,7 @@ protected:
void internalDraw(const Point& p, int layer);
uint32 m_id;
Position m_position;
Position m_pos;
ThingType *m_type;
int m_xPattern, m_yPattern, m_zPattern, m_animation;

View File

@ -33,7 +33,7 @@
Tile::Tile(const Position& position)
{
m_drawElevation = 0;
m_position = position;
m_pos = position;
}
void Tile::draw(const Point& p, const Rect& visibleRect)
@ -67,7 +67,7 @@ void Tile::draw(const Point& p, const Rect& visibleRect)
//TODO: this algorithm is slowing down render too much, but it could be cached to improve framerate
for(int xi = -1; xi <= 1; ++xi) {
for(int yi = -1; yi <= 1; ++yi) {
for(CreaturePtr creature : g_map.getTile(m_position + Position(xi, yi, 0))->getCreatures()) {
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);
@ -197,6 +197,14 @@ ItemPtr Tile::getGround()
return nullptr;
}
int Tile::getGroundSpeed()
{
int groundSpeed = 100;
if(ItemPtr ground = getGround())
groundSpeed = ground->getGroundSpeed();
return groundSpeed;
}
ThingPtr Tile::getTopLookThing()
{
if(isEmpty())

View File

@ -50,10 +50,11 @@ public:
CreaturePtr getTopCreature();
ThingPtr getTopMultiUseThing();
const Position& getPos() { return m_position; }
const Position& getPos() { return m_pos; }
int getDrawElevation() { return m_drawElevation; }
std::vector<CreaturePtr> getCreatures();
ItemPtr getGround();
int getGroundSpeed();
bool isWalkable();
bool isFullGround();
bool isFullyOpaque();
@ -67,7 +68,7 @@ public:
private:
std::vector<EffectPtr> m_effects; // Leave this outside m_things because it has no stackpos.
std::vector<ThingPtr> m_things;
Position m_position;
Position m_pos;
int m_drawElevation;
};

View File

@ -186,6 +186,8 @@ void OTClient::registerLuaFunctions()
g_lua.bindClassStaticFunction<Game>("open", std::bind(&Game::open, &g_game, _1, _2));
g_lua.bindClassStaticFunction<Game>("use", std::bind(&Game::use, &g_game, _1));
g_lua.bindClassStaticFunction<Game>("useWith", std::bind(&Game::useWith, &g_game, _1, _2));
g_lua.bindClassStaticFunction<Game>("walk", std::bind(&Game::walk, &g_game, _1));
g_lua.bindClassStaticFunction<Game>("forceWalk", std::bind(&Game::forceWalk, &g_game, _1));
g_lua.bindClassStaticFunction<Game>("attack", std::bind(&Game::attack, &g_game, _1));
g_lua.bindClassStaticFunction<Game>("cancelAttack", std::bind(&Game::cancelAttack, &g_game));
g_lua.bindClassStaticFunction<Game>("follow", std::bind(&Game::follow, &g_game, _1));

View File

@ -419,13 +419,11 @@ void ProtocolGame::parseCreatureMove(InputMessage& msg)
return;
}
g_game.processCreatureMove(creature, oldPos, newPos);
// update map tiles
g_map.removeThing(thing);
g_map.addThing(thing, newPos);
//g_game.processCreatureMove(creature, oldPos, newPos);
g_game.processCreatureMove(creature, oldPos, newPos);
}
void ProtocolGame::parseOpenContainer(InputMessage& msg)

View File

@ -25,9 +25,9 @@
#include <framework/ui/uilineedit.h>
#include <framework/platform/platformwindow.h>
bool UIGame::onKeyPress(uchar keyCode, std::string keyText, int keyboardModifiers)
bool UIGame::onKeyPress(uchar keyCode, int keyboardModifiers, bool wouldFilter)
{
if(UIWidget::onKeyPress(keyCode, keyText, keyboardModifiers))
if(UIWidget::onKeyPress(keyCode, keyboardModifiers, wouldFilter))
return true;
UILineEditPtr chatLineEdit = std::dynamic_pointer_cast<UILineEdit>(getParent()->recursiveGetChildById("consoleLineEdit"));
@ -106,10 +106,15 @@ bool UIGame::onKeyPress(uchar keyCode, std::string keyText, int keyboardModifier
}
}
if(!keyText.empty() && (keyboardModifiers == Fw::KeyboardNoModifier || keyboardModifiers == Fw::KeyboardShiftModifier)) {
return false;
}
bool UIGame::onKeyText(const std::string& keyText)
{
if(UIWidget::onKeyText(keyText))
return true;
UILineEditPtr chatLineEdit = std::dynamic_pointer_cast<UILineEdit>(getParent()->recursiveGetChildById("consoleLineEdit"));
chatLineEdit->appendText(keyText);
return true;
}
return false;
}

View File

@ -29,8 +29,8 @@
class UIGame : public UIWidget
{
protected:
virtual bool onKeyPress(uchar keyCode, std::string keyText, int keyboardModifiers);
bool onKeyPress(uchar keyCode, int keyboardModifiers, bool wouldFilter);
bool onKeyText(const std::string& keyText);
};
#endif

View File

@ -109,7 +109,7 @@ public:
bool operator==(const Position& other) const { return other.x == x && other.y == y && other.z == z; }
bool operator!=(const Position& other) const { return other.x!=x || other.y!=y || other.z!=z; }
bool isInRange(const Position& pos, int xdif, int ydif, int zdif = 1) {
bool isInRange(const Position& pos, int xdif, int ydif, int zdif = 1) const {
return std::abs(x-pos.x) <= xdif && std::abs(y-pos.y) <= ydif && std::abs(pos.z-z) <= zdif;
}