diff --git a/data/styles/10-buttons.otui b/data/styles/10-buttons.otui index fe5e60d6..8457df93 100644 --- a/data/styles/10-buttons.otui +++ b/data/styles/10-buttons.otui @@ -24,7 +24,7 @@ TabButton < UIButton image-source: /images/ui/tabbutton_rounded image-color: white image-clip: 0 0 20 20 - image-border: 2 + image-border: 1 icon-color: white color: #aaaaaa diff --git a/data/styles/10-checkboxes.otui b/data/styles/10-checkboxes.otui index 42dda230..e0823355 100644 --- a/data/styles/10-checkboxes.otui +++ b/data/styles/10-checkboxes.otui @@ -48,7 +48,7 @@ ButtonBox < UICheckBox image-source: /images/ui/tabbutton_rounded image-color: white image-clip: 0 0 20 20 - image-border: 2 + image-border: 3 $hover !disabled: image-clip: 0 20 20 20 diff --git a/data/styles/20-tabbars.otui b/data/styles/20-tabbars.otui index 7fcd4854..a356921b 100644 --- a/data/styles/20-tabbars.otui +++ b/data/styles/20-tabbars.otui @@ -1,18 +1,57 @@ +MoveableTabBar < UIMoveableTabBar + size: 80 20 +MoveableTabBarPanel < Panel +MoveableTabBarButton < UIButton + size: 20 20 + image-source: /images/ui/tabbutton_square + image-color: white + image-clip: 0 0 20 20 + image-border: 3 + icon-color: white + color: #aaaaaa + anchors.top: parent.top + anchors.left: parent.left + padding: 5 + + $hover !checked: + image-clip: 0 20 20 20 + color: white + + $disabled: + image-color: #ffffff66 + icon-color: #888888 + + $checked: + image-clip: 0 40 20 20 + color: #D8E7F0 + + $on !checked: + color: #F55E5E + TabBar < UITabBar size: 80 20 + Panel + id: buttonsPanel + anchors.fill: parent TabBarPanel < Panel TabBarButton < UIButton size: 20 20 image-source: /images/ui/tabbutton_square image-color: white image-clip: 0 0 20 20 - image-border: 2 + image-border: 3 icon-color: white color: #aaaaaa anchors.top: parent.top - anchors.left: parent.left padding: 5 + $first: + anchors.left: parent.left + + $!first: + anchors.left: prev.right + margin-left: 5 + $hover !checked: image-clip: 0 20 20 20 color: white @@ -23,7 +62,7 @@ TabBarButton < UIButton $checked: image-clip: 0 40 20 20 - color: #D8E7F0 + color: #80c7f8 $on !checked: color: #F55E5E @@ -35,7 +74,6 @@ TabBarRoundedButton < TabBarButton TabBarVertical < UITabBar width: 96 - ScrollableFlatPanel id: buttonsPanel anchors.top: parent.top @@ -43,7 +81,6 @@ TabBarVertical < UITabBar anchors.right: scrollBar.left anchors.bottom: parent.bottom vertical-scrollbar: scrollBar - VerticalScrollBar id: scrollBar anchors.top: parent.top @@ -51,12 +88,9 @@ TabBarVertical < UITabBar anchors.right: parent.right step: 16 pixels-scroll: true - $!on: width: 0 - TabBarVerticalPanel < Panel - TabBarVerticalButton < UIButton size: 48 48 color: #aaaaaa @@ -66,24 +100,18 @@ TabBarVerticalButton < UIButton icon-align: top icon-offset-y: 2 icon-color: #888888 - $first: anchors.top: parent.top - $!first: anchors.top: prev.bottom margin-top: 5 - $hover !checked: color: white icon-color: #cccccc - $disabled: icon-color: #333333 - $checked: icon-color: #ffffff color: #80c7f8 - $on !checked: color: #F55E5E diff --git a/data/things/.gitignore b/data/things/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/data/things/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/modules/client_entergame/characterlist.lua b/modules/client_entergame/characterlist.lua index 9629dfbe..634cc189 100644 --- a/modules/client_entergame/characterlist.lua +++ b/modules/client_entergame/characterlist.lua @@ -30,7 +30,8 @@ local function tryLogin(charInfo, tries) CharacterList.destroyLoadBox() - g_game.loginWorld(G.account, G.password, charInfo.worldName, charInfo.worldHost, charInfo.worldPort, charInfo.characterName) + local locale = modules.client_locales.getCurrentLocale().name + g_game.loginWorld(G.account, G.password, charInfo.worldName, charInfo.worldHost, charInfo.worldPort, charInfo.characterName, locale) loadBox = displayCancelBox(tr('Please wait'), tr('Connecting to game server...')) connect(loadBox, { onCancel = function() @@ -82,6 +83,7 @@ local function resendWait() if selected then local charInfo = { worldHost = selected.worldHost, worldPort = selected.worldPort, + worldName = selected.worldName, characterName = selected.characterName } tryLogin(charInfo) end @@ -119,9 +121,19 @@ function onGameConnectionError(message, code) end end +function onGameUpdateNeeded(signature) + CharacterList.destroyLoadBox() + errorBox = displayErrorBox(tr("Update needed"), tr('Enter with your account again to update your client.')) + errorBox.onOk = function() + errorBox = nil + CharacterList.showAgain() + end +end + -- public functions function CharacterList.init() connect(g_game, { onLoginError = onGameLoginError }) + connect(g_game, { onUpdateNeeded = onGameUpdateNeeded }) connect(g_game, { onConnectionError = onGameConnectionError }) connect(g_game, { onGameStart = CharacterList.destroyLoadBox }) connect(g_game, { onLoginWait = onLoginWait }) @@ -134,6 +146,7 @@ end function CharacterList.terminate() disconnect(g_game, { onLoginError = onGameLoginError }) + disconnect(g_game, { onUpdateNeeded = onGameUpdateNeeded }) disconnect(g_game, { onConnectionError = onGameConnectionError }) disconnect(g_game, { onGameStart = CharacterList.destroyLoadBox }) disconnect(g_game, { onLoginWait = onLoginWait }) @@ -208,6 +221,7 @@ function CharacterList.create(characters, account, otui) -- these are used by login widget.characterName = characterInfo.name + widget.worldName = characterInfo.worldName widget.worldHost = characterInfo.worldIp widget.worldPort = characterInfo.worldPort @@ -281,6 +295,7 @@ function CharacterList.doLogin() if selected then local charInfo = { worldHost = selected.worldHost, worldPort = selected.worldPort, + worldName = selected.worldName, characterName = selected.characterName } charactersWindow:hide() tryLogin(charInfo) diff --git a/modules/client_entergame/entergame.lua b/modules/client_entergame/entergame.lua index 7506ac66..ab5a075b 100644 --- a/modules/client_entergame/entergame.lua +++ b/modules/client_entergame/entergame.lua @@ -58,6 +58,17 @@ local function onChangeProtocol(combobox, option) protocolBox:setTooltip("Supports Client" .. (#clients > 1 and "s" or "") .. ": " .. table.toString(clients)) end +local function onUpdateNeeded(protocol, signature) + loadBox:destroy() + loadBox = nil + + if updateFunc then + local continueFunc = EnterGame.show + local cancelFunc = EnterGame.show + updateFunc(signature, continueFunc, cancelFunc) + end +end + -- public functions function EnterGame.init() enterGame = g_ui.displayUI('entergame') @@ -118,6 +129,7 @@ end function EnterGame.terminate() g_keyboard.unbindKeyDown('Ctrl+G') + removeEvent(autoLoginEvent) enterGame:destroy() enterGame = nil enterGameButton:destroy() @@ -183,6 +195,7 @@ function EnterGame.doLogin() protocolLogin.onError = onError protocolLogin.onMotd = onMotd protocolLogin.onCharacterList = onCharacterList + protocolLogin.onUpdateNeeded = onUpdateNeeded loadBox = displayCancelBox(tr('Please wait'), tr('Connecting to login server...')) connect(loadBox, { onCancel = function(msgbox) @@ -197,8 +210,9 @@ function EnterGame.doLogin() g_game.setClientVersion(clientVersions[#clientVersions]) end - if modules.game_tibiafiles.isLoaded() then - protocolLogin:login(G.host, G.port, G.account, G.password) + if modules.game_things.isLoaded() then + local locale = modules.client_locales.getCurrentLocale().name + protocolLogin:login(G.host, G.port, G.account, G.password, locale) else loadBox:destroy() loadBox = nil @@ -264,3 +278,9 @@ function EnterGame.setUniqueServer(host, port, protocol, windowWidth, windowHeig if not windowHeight then windowHeight = 200 end enterGame:setHeight(windowHeight) end + +function EnterGame.setServerInfo(message) + local label = enterGame:getChildById('serverInfoLabel') + label:setText(message) +end + diff --git a/modules/client_entergame/entergame.otui b/modules/client_entergame/entergame.otui index ddb76c9b..20b78dca 100644 --- a/modules/client_entergame/entergame.otui +++ b/modules/client_entergame/entergame.otui @@ -12,7 +12,7 @@ EnterGameWindow anchors.top: parent.top text-auto-resize: true - TextEdit + PasswordTextEdit id: accountNameTextEdit anchors.left: parent.left anchors.right: parent.right @@ -86,7 +86,7 @@ EnterGameWindow CheckBox id: rememberPasswordBox !text: tr('Remember password') - !tooltip: tr('Remember account and password when starts otclient') + !tooltip: tr('Remember account and password when starts client') anchors.left: parent.left anchors.right: parent.right anchors.top: prev.bottom @@ -97,7 +97,7 @@ EnterGameWindow id: autoLoginBox enabled: false !text: tr('Auto login') - !tooltip: tr('Open charlist automatically when starting otclient') + !tooltip: tr('Open charlist automatically when starting client') anchors.left: parent.left anchors.right: parent.right anchors.top: prev.bottom @@ -109,3 +109,10 @@ EnterGameWindow anchors.right: parent.right anchors.bottom: parent.bottom @onClick: EnterGame.doLogin() + + Label + id: serverInfoLabel + font: verdana-11px-rounded + anchors.bottom: parent.bottom + anchors.left: parent.left + color: green diff --git a/modules/client_locales/locales.lua b/modules/client_locales/locales.lua index a4ae4de6..38172081 100644 --- a/modules/client_locales/locales.lua +++ b/modules/client_locales/locales.lua @@ -5,12 +5,10 @@ local defaultLocaleName = 'en' local installedLocales local currentLocale -LocaleExtendedId = 1 - function sendLocale(localeName) local protocolGame = g_game.getProtocolGame() if protocolGame then - protocolGame:sendExtendedOpcode(LocaleExtendedId, localeName) + protocolGame:sendExtendedOpcode(ExtendedIds.Locale, localeName) return true end return false @@ -66,7 +64,7 @@ function init() connect(g_app, {onRun = createWindow}) end - ProtocolGame.registerExtendedOpcode(LocaleExtendedId, onExtendedLocales) + ProtocolGame.registerExtendedOpcode(ExtendedIds.Locale, onExtendedLocales) connect(g_game, { onGameStart = onGameStart }) end @@ -74,7 +72,7 @@ function terminate() installedLocales = nil currentLocale = nil - ProtocolGame.unregisterExtendedOpcode(LocaleExtendedId) + ProtocolGame.unregisterExtendedOpcode(ExtendedIds.Locale) disconnect(g_game, { onGameStart = onGameStart }) end diff --git a/modules/client_locales/neededtranslations.lua b/modules/client_locales/neededtranslations.lua index d4ef0c49..a759579c 100644 --- a/modules/client_locales/neededtranslations.lua +++ b/modules/client_locales/neededtranslations.lua @@ -1,317 +1,3 @@ -- generated by ./tools/gen_needed_translations.sh modules.client_locales.neededTranslations = { - "1a) Offensive Name", - "1b) Invalid Name Format", - "1c) Unsuitable Name", - "1d) Name Inciting Rule Violation", - "2a) Offensive Statement", - "2b) Spamming", - "2c) Illegal Advertising", - "2d) Off-Topic Public Statement", - "2e) Non-English Public Statement", - "2f) Inciting Rule Violation", - "3a) Bug Abuse", - "3b) Game Weakness Abuse", - "3c) Using Unofficial Software to Play", - "3d) Hacking", - "3e) Multi-Clienting", - "3f) Account Trading or Sharing", - "4a) Threatening Gamemaster", - "4b) Pretending to Have Influence on Rule Enforcement", - "4c) False Report to Gamemaster", - "Accept", - "Account name", - "Account Status:\nFree Account", - "Account Status:\nPremium Account (%s) days left", - "Action:", - "Add", - "Add new VIP", - "Addon 1", - "Addon 2", - "Addon 3", - "Add to VIP list", - "Adjust volume", - "Alas! Brave adventurer, you have met a sad fate.\nBut do not despair, for the gods will bring you back\ninto this world in exchange for a small sacrifice\n\nSimply click on Ok to resume your journeys!", - "All modules and scripts were reloaded.", - "Allow auto chase override", - "Amount:", - "Amount", - "Anonymous", - "Are you sure you want to logout?", - "Attack", - "Author", - "Autoload", - "Autoload priority", - "Auto login", - "Auto login selected character on next charlist load", - "Axe Fighting", - "Balance:", - "Banishment", - "Banishment + Final Warning", - "Battle", - "Browse", - "Bug report sent.", - "Button Assign", - "Buy", - "Buy Now", - "Buy Offers", - "Buy with backpack", - "Cancel", - "Cannot login while already in game.", - "Cap", - "Capacity", - "Center", - "Channels", - "Character List", - "Classic control", - "Clear current message window", - "Clear object", - "Client needs update.", - "Close", - "Close this channel", - "Club Fighting", - "Combat Controls", - "Comment:", - "Connecting to game server...", - "Connecting to login server...", - "Copy message", - "Copy name", - "Copy Name", - "Create New Offer", - "Create Offer", - "Current hotkeys:", - "Current hotkey to add: %s", - "Current Offers", - "Default", - "Description", - "Destructive Behaviour", - "Detail", - "Details", - "Disable Shared Experience", - "Distance Fighting", - "Edit hotkey text:", - "Edit List", - "Edit Text", - "Enable music", - "Enable Shared Experience", - "Enable vertical synchronization", - "Enter Game", - "Enter one name per line.", - "Error", - "Error", - "Excessive Unjustified Player Killing", - "Exclude from private chat", - "Exit", - "Experience", - "Filter list to match your level", - "Filter list to match your vocation", - "Fishing", - "Fist Fighting", - "Follow", - "Fullscreen", - "Game framerate limit: %s", - "General", - "Graphics", - "Graphics Engine:", - "Head", - "Health Info", - "Health Information", - "Hide monsters", - "Hide non-skull players", - "Hide Npcs", - "Hide party members", - "Hide players", - "Hit Points", - "Hold right mouse button to navigate\nScroll mouse middle button to zoom", - "Hotkeys", - "If you shut down the program, your character might stay in the game.\nClick on 'Logout' to ensure that you character leaves the game properly.\nClick on 'Exit' if you want to exit the program without logging out your character.", - "Ignore capacity", - "Ignore equipped", - "Interface framerate limit: %s", - "Inventory", - "Invite to Party", - "Invite to private chat", - "IP Address Banishment", - "Item Offers", - "It is empty.\n", - "Join %s\'s Party", - "Leave Party", - "Level", - "Limits FPS to 60", - "List of items that you're able to buy", - "List of items that you're able to sell", - "Load", - "Location", - "Logging out...", - "Login", - "Login Error", - "Login Error", - "Logout", - "Look", - "Magic Level", - "Make sure that your client uses\nthe correct game protocol version", - "Mana", - "Manage hotkeys:", - "Market", - "Market Offers", - "Message of the day", - "Message to ", - "Message to %s", - "Minimap", - "Module Manager", - "Module name", - "Move Stackable Item", - "Move up", - "My Offers", - "Name:", - "Name", - "Name Report", - "Name Report + Banishment", - "Name Report + Banishment + Final Warning", - "No", - "No item selected.", - "No Mount", - "No Outfit", - "No statement has been selected.", - "Notation", - "NPC Trade", - "Offer History", - "Offers", - "Offer Type:", - "Ok", - "Okay", - "on %s.\n", - "Open", - "Open a private message channel:", - "Open charlist automatically when starting otclient", - "Open in new window", - "Open new channel", - "Options", - "Particles Manager", - "Pass Leadership to %s", - "Password", - "Pause", - "Piece Price:", - "Please enter a character name:", - "Please, press the key you wish to add onto your hotkeys manager", - "Please Select", - "Please use this dialog to only report bugs. Do not report rule violations here!", - "Please wait", - "Port", - "Preview", - "Price:", - "Primary", - "Protocol", - "Quantity:", - "Quest Log", - "Randomize", - "Randomize characters outfit", - "Reason:", - "Refresh", - "Reject", - "Reload", - "Reload All", - "Remember account and password when starts otclient", - "Remember password", - "Remove", - "Remove %s", - "Report Bug", - "Revoke %s\'s Invitation", - "Rotate", - "Rule Violation", - "Search:", - "Search", - "Secondary", - "Select object", - "Select Outfit", - "Sell", - "Sell Now", - "Sell Offers", - "Send", - "Send automatically", - "Server", - "Server Log", - "Set Outfit", - "Shielding", - "Show all items", - "Show Depot Only", - "Show event messages in console", - "Show frame rate", - "Show info messages in console", - "Show left panel", - "Show levels in console", - "Show private messages in console", - "Show private messages on screen", - "Show status messages in console", - "Show Text", - "Show timestamps in console", - "Show your depot items only", - "Skills", - "Soul", - "Soul Points", - "Stamina", - "Start", - "Statement:", - "Statement Report", - "Statistics", - "Stop Attack", - "Stop Follow", - "%s: (use object)", - "%s: (use object on target)", - "%s: (use object on yourself)", - "%s: (use object with crosshair)", - "Sword Fighting", - "Terminal", - "There is no way.", - "Total Price:", - "Trade", - "Trade with ...", - "Trying to reconnect in %s seconds.", - "Unable to load dat file, please place a valid dat in '%s'", - "Unable to load spr file, please place a valid spr in '%s'", - "Unable to logout.", - "Unload", - "Use", - "Use on target", - "Use on yourself", - "Use with ...", - "Version", - "VIP list", - "VIP List", - "Voc.", - "Waiting List", - "Website", - "Weight:", - "With crosshair", - "Yes", - "You are bleeding", - "You are burning", - "You are cursed", - "You are dazzled", - "You are dead.", - "You are dead", - "You are drowing", - "You are drunk", - "You are electrified", - "You are freezing", - "You are hasted", - "You are hungry", - "You are paralysed", - "You are poisoned", - "You are protected by a magic shield", - "You are strengthened", - "You are within a protection zone", - "You can enter new text.", - "You have %s percent", - "You have %s percent to go", - "You may not logout during a fight", - "You may not logout or enter a protection zone", - "You must enter a comment.", - "You must enter an account name and password.", - "You must enter a valid server address and port.", - "You must select a character to login!", - "Your Capacity:", - "You read the following, written by \n%s\n", - "You read the following, written on %s.\n", - "Your Money:", } diff --git a/modules/corelib/ui/uimovabletabbar.lua b/modules/corelib/ui/uimovabletabbar.lua new file mode 100644 index 00000000..c2c03a66 --- /dev/null +++ b/modules/corelib/ui/uimovabletabbar.lua @@ -0,0 +1,246 @@ +-- @docclass +UIMoveableTabBar = extends(UIWidget) + +-- private functions +local function onTabClick(tab) + tab.tabBar:selectTab(tab) +end + +local function updateMargins(tabBar, ignored) + if #tabBar.tabs == 0 then return end + + local currentMargin = 0 + for i = 1, #tabBar.tabs do + if tabBar.tabs[i] ~= ignored then + if i == 1 then + tabBar.tabs[i]:setMarginLeft(0) + else + tabBar.tabs[i]:setMarginLeft(tabBar.tabSpacing * (i - 1) + currentMargin) + end + end + currentMargin = currentMargin + tabBar.tabs[i]:getWidth() + end +end + +local function onTabMousePress(tab, mousePos, mouseButton) + if mouseButton == MouseRightButton then + if tab.menuCallback then tab.menuCallback(tab, mousePos, mouseButton) end + return true + end +end + +local function onTabDragEnter(tab) + tab:raise() + tab.tabBar.selected = tab + return true +end + +local function onTabDragLeave(tab) + updateMargins(tab.tabBar) + tab.tabBar.selected = nil + return true +end + +local function onTabDragMove(tab, mousePos, mouseMoved) + if tab == tab.tabBar.selected then + local newMargin = tab:getMarginLeft() + mouseMoved.x + if newMargin >= -tab.tabBar.tabSpacing and newMargin < tab.tabBar:getWidth() - tab:getWidth() then + tab:setMarginLeft(newMargin) + end + + local tabs = tab.tabBar.tabs + local lastMargin = -tab.tabBar.tabSpacing + for i = 1, #tabs do + local nextMargin = tabs[i + 1] and (tabs[i + 1] == tab and (tabs[i]:getMarginLeft() + tabs[i]:getWidth() + tab.tabBar.tabSpacing) or tabs[i + 1]:getMarginLeft()) or tab.tabBar:getWidth() + if (tab:getMarginLeft()+(tabs[i]:getWidth()/2)) >= lastMargin and (tab:getMarginLeft()+(tabs[i]:getWidth()/2)) < nextMargin then + if tabs[i] ~= tab then + local newIndex = table.find(tab.tabBar.tabs, tab.tabBar.tabs[i]) + table.remove(tab.tabBar.tabs, table.find(tab.tabBar.tabs, tab)) + table.insert(tab.tabBar.tabs, newIndex, tab) + updateMargins(tab.tabBar, tab) + break + else + updateMargins(tab.tabBar, tab) + break + end + end + lastMargin = tab.tabBar.tabs[i]:getMarginLeft() == 0 and -tab.tabBar.tabSpacing or tab.tabBar.tabs[i]:getMarginLeft() + end + end +end + +local function tabBlink(tab) + if not tab.blinking then return end + tab:setOn(not tab:isOn()) + tab.blinkEvent = scheduleEvent(function() tabBlink(tab) end, 500) +end + +-- public functions +function UIMoveableTabBar.create() + local tabbar = UIMoveableTabBar.internalCreate() + tabbar:setFocusable(false) + tabbar.tabs = {} + tabbar.selected = nil -- dragged tab + tabbar.tabSpacing = 5 + tabbar.tabsMoveable = false + return tabbar +end + +function UIMoveableTabBar:setContentWidget(widget) + self.contentWidget = widget + if #self.tabs > 0 then + self.contentWidget:addChild(self.tabs[1].tabPanel) + end +end + +function UIMoveableTabBar:setTabSpacing(tabSpacing) + self.tabSpacing = tabSpacing + updateMargins(self) +end + +function UIMoveableTabBar:addTab(text, panel, menuCallback) + if panel == nil then + panel = g_ui.createWidget(self:getStyleName() .. 'Panel') + panel:setId('tabPanel') + end + + local tab = g_ui.createWidget(self:getStyleName() .. 'Button', self) + panel.isTab = true + tab.tabPanel = panel + tab.tabBar = self + tab:setId('tab') + tab:setDraggable(self.tabsMoveable) + tab:setText(text) + tab:setWidth(tab:getTextSize().width + tab:getPaddingLeft() + tab:getPaddingRight()) + tab.menuCallback = menuCallback or nil + tab.onClick = onTabClick + tab.onMousePress = onTabMousePress + tab.onDragEnter = onTabDragEnter + tab.onDragLeave = onTabDragLeave + tab.onDragMove = onTabDragMove + tab.onDestroy = function() tab.tabPanel:destroy() end + + table.insert(self.tabs, tab) + if #self.tabs == 1 then + self:selectTab(tab) + tab:setMarginLeft(0) + else + local newMargin = self.tabSpacing * (#self.tabs - 1) + for i = 1, #self.tabs - 1 do + newMargin = newMargin + self.tabs[i]:getWidth() + end + tab:setMarginLeft(newMargin) + end + + return tab +end + +-- Additional function to move the tab by lua +function UIMoveableTabBar:moveTab(tab, units) + local index = table.find(self.tabs, tab) + if index == nil then return end + + local focus = false + if self.currentTab == tab then + self:selectPrevTab() + focus = true + end + + table.remove(self.tabs, index) + + local newIndex = math.min(#self.tabs+1, math.max(index + units, 1)) + table.insert(self.tabs, newIndex, tab) + if focus then self:selectTab(tab) end + updateMargins(self) + return newIndex +end + +function UIMoveableTabBar:onStyleApply(styleName, styleNode) + if styleNode['moveable'] then + self.tabsMoveable = styleNode['moveable'] + end +end + +function UIMoveableTabBar:removeTab(tab) + local index = table.find(self.tabs, tab) + if index == nil then return end + if self.currentTab == tab then + self:selectPrevTab() + end + table.remove(self.tabs, index) + if tab.blinkEvent then + removeEvent(tab.blinkEvent) + end + tab:destroy() + updateMargins(self) +end + +function UIMoveableTabBar:getTab(text) + for k,tab in pairs(self.tabs) do + if tab:getText():lower() == text:lower() then + return tab + end + end +end + +function UIMoveableTabBar:selectTab(tab) + if self.currentTab == tab then return end + if self.contentWidget then + local selectedWidget = self.contentWidget:getLastChild() + if selectedWidget and selectedWidget.isTab then + self.contentWidget:removeChild(selectedWidget) + end + self.contentWidget:addChild(tab.tabPanel) + tab.tabPanel:fill('parent') + end + + if self.currentTab then + self.currentTab:setChecked(false) + end + signalcall(self.onTabChange, self, tab) + self.currentTab = tab + tab:setChecked(true) + tab:setOn(false) + tab.blinking = false + + local parent = tab:getParent() + parent:focusChild(tab, MouseFocusReason) +end + +function UIMoveableTabBar:selectNextTab() + if self.currentTab == nil then return end + local index = table.find(self.tabs, self.currentTab) + if index == nil then return end + local nextTab = self.tabs[index + 1] or self.tabs[1] + if not nextTab then return end + self:selectTab(nextTab) +end + +function UIMoveableTabBar:selectPrevTab() + if self.currentTab == nil then return end + local index = table.find(self.tabs, self.currentTab) + if index == nil then return end + local prevTab = self.tabs[index - 1] or self.tabs[#self.tabs] + if not prevTab then return end + self:selectTab(prevTab) +end + +function UIMoveableTabBar:blinkTab(tab) + if tab:isChecked() or tab.blinking then return end + tab.blinking = true + tabBlink(tab) +end + +function UIMoveableTabBar:getTabPanel(tab) + return tab.tabPanel +end + +function UIMoveableTabBar:getCurrentTabPanel() + if self.currentTab then + return self.currentTab.tabPanel + end +end + +function UIMoveableTabBar:getCurrentTab() + return self.currentTab +end diff --git a/modules/corelib/ui/uitabbar.lua b/modules/corelib/ui/uitabbar.lua index 18f2c553..79c67d53 100644 --- a/modules/corelib/ui/uitabbar.lua +++ b/modules/corelib/ui/uitabbar.lua @@ -6,86 +6,24 @@ local function onTabClick(tab) tab.tabBar:selectTab(tab) end -local function updateMargins(tabBar, ignored) - if #tabBar.tabs == 0 then return end - - local currentMargin = 0 - for i = 1, #tabBar.tabs do - if tabBar.tabs[i] ~= ignored then - if i == 1 then - tabBar.tabs[i]:setMarginLeft(0) - else - tabBar.tabs[i]:setMarginLeft(tabBar.tabSpacing * (i - 1) + currentMargin) - end - end - currentMargin = currentMargin + tabBar.tabs[i]:getWidth() - end -end - -local function onTabMousePress(tab, mousePos, mouseButton) - if mouseButton == MouseRightButton then - if tab.menuCallback then tab.menuCallback(tab, mousePos, mouseButton) end - return true - end -end - -local function onTabDragEnter(tab) - tab:raise() - tab.tabBar.selected = tab - return true -end - -local function onTabDragLeave(tab) - updateMargins(tab.tabBar) - tab.tabBar.selected = nil - return true -end - -local function onTabDragMove(tab, mousePos, mouseMoved) - if tab == tab.tabBar.selected then - local newMargin = tab:getMarginLeft() + mouseMoved.x - if newMargin >= -tab.tabBar.tabSpacing and newMargin < tab.tabBar:getWidth() - tab:getWidth() then - tab:setMarginLeft(newMargin) - end - - local tabs = tab.tabBar.tabs - local lastMargin = -tab.tabBar.tabSpacing - for i = 1, #tabs do - local nextMargin = tabs[i + 1] and (tabs[i + 1] == tab and (tabs[i]:getMarginLeft() + tabs[i]:getWidth() + tab.tabBar.tabSpacing) or tabs[i + 1]:getMarginLeft()) or tab.tabBar:getWidth() - if (tab:getMarginLeft()+(tabs[i]:getWidth()/2)) >= lastMargin and (tab:getMarginLeft()+(tabs[i]:getWidth()/2)) < nextMargin then - if tabs[i] ~= tab then - local newIndex = table.find(tab.tabBar.tabs, tab.tabBar.tabs[i]) - table.remove(tab.tabBar.tabs, table.find(tab.tabBar.tabs, tab)) - table.insert(tab.tabBar.tabs, newIndex, tab) - updateMargins(tab.tabBar, tab) - break - else - updateMargins(tab.tabBar, tab) - break - end - end - lastMargin = tab.tabBar.tabs[i]:getMarginLeft() == 0 and -tab.tabBar.tabSpacing or tab.tabBar.tabs[i]:getMarginLeft() - end +local function onTabMouseRelease(tab, mousePos, mouseButton) + if mouseButton == MouseRightButton and tab:containsPoint(mousePos) then + signalcall(tab.tabBar.onTabLeftClick, tab.tabBar, tab) end end -local function tabBlink(tab) - if not tab.blinking then return end - tab:setOn(not tab:isOn()) - tab.blinkEvent = scheduleEvent(function() tabBlink(tab) end, 500) -end - -- public functions function UITabBar.create() local tabbar = UITabBar.internalCreate() tabbar:setFocusable(false) tabbar.tabs = {} - tabbar.selected = nil -- dragged tab - tabbar.tabSpacing = 5 - tabbar.tabsMoveable = false return tabbar end +function UITabBar:onSetup() + self.buttonsPanel = self:getChildById('buttonsPanel') +end + function UITabBar:setContentWidget(widget) self.contentWidget = widget if #self.tabs > 0 then @@ -93,72 +31,33 @@ function UITabBar:setContentWidget(widget) end end -function UITabBar:setTabSpacing(tabSpacing) - self.tabSpacing = tabSpacing - updateMargins(self) -end - -function UITabBar:addTab(text, panel, menuCallback) +function UITabBar:addTab(text, panel, icon) if panel == nil then panel = g_ui.createWidget(self:getStyleName() .. 'Panel') panel:setId('tabPanel') end - local tab = g_ui.createWidget(self:getStyleName() .. 'Button', self) + local tab = g_ui.createWidget(self:getStyleName() .. 'Button', self.buttonsPanel) panel.isTab = true tab.tabPanel = panel tab.tabBar = self tab:setId('tab') - tab:setDraggable(self.tabsMoveable) tab:setText(text) tab:setWidth(tab:getTextSize().width + tab:getPaddingLeft() + tab:getPaddingRight()) - tab.menuCallback = menuCallback or nil tab.onClick = onTabClick - tab.onMousePress = onTabMousePress - tab.onDragEnter = onTabDragEnter - tab.onDragLeave = onTabDragLeave - tab.onDragMove = onTabDragMove + tab.onMouseRelease = onTabMouseRelease tab.onDestroy = function() tab.tabPanel:destroy() end table.insert(self.tabs, tab) if #self.tabs == 1 then self:selectTab(tab) - tab:setMarginLeft(0) - else - local newMargin = self.tabSpacing * (#self.tabs - 1) - for i = 1, #self.tabs - 1 do - newMargin = newMargin + self.tabs[i]:getWidth() - end - tab:setMarginLeft(newMargin) end - return tab -end - --- Additional function to move the tab by lua -function UITabBar:moveTab(tab, units) - local index = table.find(self.tabs, tab) - if index == nil then return end - - local focus = false - if self.currentTab == tab then - self:selectPrevTab() - focus = true - end - - table.remove(self.tabs, index) - - local newIndex = math.min(#self.tabs+1, math.max(index + units, 1)) - table.insert(self.tabs, newIndex, tab) - if focus then self:selectTab(tab) end - updateMargins(self) - return newIndex -end + local tabStyle = {} + tabStyle['icon-source'] = icon + tab:mergeStyle(tabStyle) -function UITabBar:onStyleApply(styleName, styleNode) - if styleNode['moveable'] then - self.tabsMoveable = styleNode['moveable'] - end + return tab end function UITabBar:removeTab(tab) @@ -168,11 +67,7 @@ function UITabBar:removeTab(tab) self:selectPrevTab() end table.remove(self.tabs, index) - if tab.blinkEvent then - removeEvent(tab.blinkEvent) - end tab:destroy() - updateMargins(self) end function UITabBar:getTab(text) @@ -201,7 +96,6 @@ function UITabBar:selectTab(tab) self.currentTab = tab tab:setChecked(true) tab:setOn(false) - tab.blinking = false local parent = tab:getParent() parent:focusChild(tab, MouseFocusReason) @@ -225,12 +119,6 @@ function UITabBar:selectPrevTab() self:selectTab(prevTab) end -function UITabBar:blinkTab(tab) - if tab:isChecked() or tab.blinking then return end - tab.blinking = true - tabBlink(tab) -end - function UITabBar:getTabPanel(tab) return tab.tabPanel end diff --git a/modules/game_battle/battle.lua b/modules/game_battle/battle.lua index cdce4a56..b2937854 100644 --- a/modules/game_battle/battle.lua +++ b/modules/game_battle/battle.lua @@ -97,29 +97,25 @@ function onMiniWindowClose() end function checkCreatures() + removeAllCreatures() + + local spectators = {} local player = g_game.getLocalPlayer() if g_game.isOnline() then creatures = g_map.getSpectators(player:getPosition(), false) for i, creature in ipairs(creatures) do if creature ~= player and doCreatureFitFilters(creature) then - if not hasCreature(creature) then - addCreature(creature) - end - end - end - local toRemove = {} - for i, b in pairs(battleButtonsByCreaturesList) do - if (not table.contains(creatures, b.creature)) or (not doCreatureFitFilters(b.creature)) then - table.insert(toRemove, b.creature) + table.insert(spectators, creature) end end - - for i, creature in pairs(toRemove) do - removeCreature(creature) - end + end + + for i, v in pairs(spectators) do + addCreature(v) end end + function doCreatureFitFilters(creature) local localPlayer = g_game.getLocalPlayer() if creature == localPlayer then @@ -129,11 +125,7 @@ function doCreatureFitFilters(creature) local pos = creature:getPosition() if not pos then return false end - if pos.z ~= localPlayer:getPosition().z or not localPlayer:hasSight(pos) then - return false - end - - if not creature:canBeSeen() then return false end + if pos.z ~= localPlayer:getPosition().z or not creature:canBeSeen() then return false end local hidePlayers = hidePlayersButton:isChecked() local hideNPCs = hideNPCsButton:isChecked() @@ -165,24 +157,32 @@ end function onCreaturePositionChange(creature, newPos, oldPos) if creature:isLocalPlayer() then - checkCreatures() + if oldPos and newPos and newPos.z ~= oldPos.z then + checkCreatures() + else + for id, creatureButton in pairs(battleButtonsByCreaturesList) do + addCreature(creatureButton.creature) + end + end else local has = hasCreature(creature) local fit = doCreatureFitFilters(creature) if has and not fit then removeCreature(creature) - elseif not has and fit then + elseif fit then addCreature(creature) end end end + function onCreatureOutfitChange(creature, outfit, oldOutfit) - if not creature:canBeSeen() then - removeCreature(creature) - elseif doCreatureFitFilters(creature) then - removeCreature(creature) - addCreature(creature) + if hasCreature(creature) then + if doCreatureFitFilters(creature) then + addCreature(creature) + else + removeCreature(creature) + end end end @@ -223,6 +223,9 @@ function addCreature(creature) else battleButton:setLifeBarPercent(creature:getHealthPercent()) end + + local localPlayer = g_game.getLocalPlayer() + battleButton:setVisible(localPlayer:hasSight(creature:getPosition()) and creature:canBeSeen()) end function removeAllCreatures() diff --git a/modules/game_console/console.otui b/modules/game_console/console.otui index edbcaf2b..436090d7 100644 --- a/modules/game_console/console.otui +++ b/modules/game_console/console.otui @@ -18,8 +18,8 @@ ConsolePhantomLabel < UILabel selection-color: #111416 selection-background-color: #999999 -ConsoleTabBar < TabBar -ConsoleTabBarPanel < TabBarPanel +ConsoleTabBar < MoveableTabBar +ConsoleTabBarPanel < MoveableTabBarPanel id: consoleTab ScrollablePanel @@ -69,7 +69,7 @@ ConsoleTabBarPanel < TabBarPanel step: 14 pixels-scroll: true -ConsoleTabBarButton < TabBarButton +ConsoleTabBarButton < MoveableTabBarButton height: 28 padding: 15 diff --git a/modules/game_interface/gameinterface.lua b/modules/game_interface/gameinterface.lua index 33ff56de..96bce697 100644 --- a/modules/game_interface/gameinterface.lua +++ b/modules/game_interface/gameinterface.lua @@ -64,11 +64,6 @@ function bindKeys() g_keyboard.bindKeyPress('Down', function() smartWalk(South) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) g_keyboard.bindKeyPress('Left', function() smartWalk(West) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) - g_keyboard.bindKeyDown('Up', function() smartWalk(North) end, gameRootPanel) - g_keyboard.bindKeyDown('Right', function() smartWalk(East) end, gameRootPanel) - g_keyboard.bindKeyDown('Down', function() smartWalk(South) end, gameRootPanel) - g_keyboard.bindKeyDown('Left', function() smartWalk(West) end, gameRootPanel) - g_keyboard.bindKeyPress('Numpad8', function() smartWalk(North) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) g_keyboard.bindKeyPress('Numpad9', function() smartWalk(NorthEast) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) g_keyboard.bindKeyPress('Numpad6', function() smartWalk(East) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) @@ -91,8 +86,8 @@ function bindKeys() g_keyboard.bindKeyDown('Ctrl+Q', logout, gameRootPanel) g_keyboard.bindKeyDown('Ctrl+L', logout, gameRootPanel) g_keyboard.bindKeyDown('Ctrl+W', function() g_map.cleanTexts() modules.game_textmessage.clearMessages() end, gameRootPanel) - g_keyboard.bindKeyDown('Ctrl+.', toggleAspectRatio, gameRootPanel) g_keyboard.bindKeyDown('Ctrl+N', function() gameMapPanel:setDrawTexts(not gameMapPanel:isDrawingTexts()) end, gameRootPanel) + g_keyboard.bindKeyDown('Ctrl+.', toggleAlternativeView, gameRootPanel) end function terminate() @@ -324,6 +319,7 @@ function onTradeWith(clickedWidget, mousePosition) end function startUseWith(thing) + if not thing then return end selectedType = 'use' selectedThing = thing mouseGrabberWidget:grabMouse() @@ -497,6 +493,7 @@ function processMouseAction(menuPosition, mouseButton, autoWalkPos, lookThing, u return true else g_game.use(useThing) + return true end return true elseif creatureThing and keyboardModifiers == KeyboardAltModifier and (mouseButton == MouseLeftButton or mouseButton == MouseRightButton) then @@ -643,3 +640,38 @@ function onLeftPanelVisibilityChange(leftPanel, visible) end end end + +function toggleAlternativeView() + if gameMapPanel:isKeepAspectRatioEnabled() then + gameMapPanel:setKeepAspectRatio(false) + gameMapPanel:setZoom(14) + gameMapPanel:fill('parent') + gameRootPanel:fill('parent') + gameLeftPanel:setImageColor('alpha') + gameRightPanel:setImageColor('alpha') + gameLeftPanel:setMarginTop(36) + gameRightPanel:setMarginTop(36) + gameLeftPanel:setOn(true) + gameLeftPanel:setVisible(true) + gameRightPanel:setOn(true) + gameBottomPanel:setImageColor('#00000099') + modules.client_topmenu.getTopMenu():setImageColor('#ffffff66') + g_game.changeMapAwareRange(24, 20) + else + gameMapPanel:setKeepAspectRatio(true) + gameMapPanel:setVisibleDimension({ width = 15, height = 11 }) + gameMapPanel:addAnchor(AnchorLeft, 'gameLeftPanel', AnchorRight) + gameMapPanel:addAnchor(AnchorRight, 'gameRightPanel', AnchorLeft) + gameMapPanel:addAnchor(AnchorBottom, 'gameBottomPanel', AnchorTop) + gameRootPanel:addAnchor(AnchorTop, 'topMenu', AnchorBottom) + gameLeftPanel:setOn(false) + gameLeftPanel:setVisible(false) + gameLeftPanel:setImageColor('white') + gameRightPanel:setImageColor('white') + gameLeftPanel:setMarginTop(0) + gameRightPanel:setMarginTop(0) + gameBottomPanel:setImageColor('white') + modules.client_topmenu.getTopMenu():setImageColor('white') + g_game.changeMapAwareRange(18, 14) + end +end diff --git a/modules/game_tibiafiles/.gitignore b/modules/game_things/.gitignore similarity index 100% rename from modules/game_tibiafiles/.gitignore rename to modules/game_things/.gitignore diff --git a/modules/game_tibiafiles/tibiafiles.lua b/modules/game_things/things.lua similarity index 77% rename from modules/game_tibiafiles/tibiafiles.lua rename to modules/game_things/things.lua index 36640729..6df3c9a7 100644 --- a/modules/game_tibiafiles/tibiafiles.lua +++ b/modules/game_things/things.lua @@ -1,4 +1,4 @@ -filename = 'Tibia' +filename = nil loaded = false function init() @@ -19,8 +19,15 @@ end function load() local version = g_game.getProtocolVersion() - local datPath = resolvepath(version .. '/' .. filename .. '.dat') - local sprPath = resolvepath(version .. '/' .. filename .. '.spr') + + local datPath, sprPath + if filename then + datPath = resolvepath('/things/' .. filename) + sprPath = resolvepath('/things/' .. filename) + else + datPath = resolvepath('/things/' .. version .. '/Tibia') + sprPath = resolvepath('/things/' .. version .. '/Tibia') + end local errorMessage = '' if not g_things.loadDat(datPath) then diff --git a/modules/game_things/things.otmod b/modules/game_things/things.otmod new file mode 100644 index 00000000..56947d2a --- /dev/null +++ b/modules/game_things/things.otmod @@ -0,0 +1,8 @@ +Module + name: game_things + description: Contains things spr and dat + reloadable: false + sandboxed: true + scripts: [things] + @onLoad: init() + @onUnload: terminate() diff --git a/modules/game_tibiafiles/tibiafiles.otmod b/modules/game_tibiafiles/tibiafiles.otmod deleted file mode 100644 index 93480cb5..00000000 --- a/modules/game_tibiafiles/tibiafiles.otmod +++ /dev/null @@ -1,8 +0,0 @@ -Module - name: game_tibiafiles - description: Contains tibia spr and dat - reloadable: false - sandboxed: true - scripts: [tibiafiles] - @onLoad: init() - @onUnload: terminate() diff --git a/modules/game_viplist/viplist.lua b/modules/game_viplist/viplist.lua index cba5b085..7ad90f07 100644 --- a/modules/game_viplist/viplist.lua +++ b/modules/game_viplist/viplist.lua @@ -10,7 +10,7 @@ function init() g_keyboard.bindKeyDown('Ctrl+P', toggle) - vipButton = modules.client_topmenu.addRightGameToggleButton('vipListButton', tr('VIP list') .. ' (Ctrl+P)', '/images/topbuttons/viplist', toggle) + vipButton = modules.client_topmenu.addRightGameToggleButton('vipListButton', tr('VIP List') .. ' (Ctrl+P)', '/images/topbuttons/viplist', toggle) vipButton:setOn(true) vipWindow = g_ui.loadUI('viplist', modules.game_interface.getRightPanel()) diff --git a/modules/gamelib/const.lua b/modules/gamelib/const.lua index 667358ba..3b4c25df 100644 --- a/modules/gamelib/const.lua +++ b/modules/gamelib/const.lua @@ -191,4 +191,10 @@ VipState = { Pending = 2 } +ExtendedIds = { + Activate = 0, + Locale = 1, + Ping = 2 +} + -- @} diff --git a/modules/gamelib/game.lua b/modules/gamelib/game.lua index 78540b95..f7a9f2e1 100644 --- a/modules/gamelib/game.lua +++ b/modules/gamelib/game.lua @@ -14,8 +14,10 @@ function g_game.chooseRsa(host) g_game.setCustomOs(OsTypes.Linux) end else + if currentRsa == CIPSOFT_RSA then + g_game.setCustomOs(-1) + end g_game.setRsa(OTSERV_RSA) - g_game.setCustomOs(-1) end end diff --git a/modules/gamelib/gamelib.otmod b/modules/gamelib/gamelib.otmod index 7b59f42e..65faf065 100644 --- a/modules/gamelib/gamelib.otmod +++ b/modules/gamelib/gamelib.otmod @@ -5,7 +5,7 @@ Module website: www.otclient.info dependencies: - - game_tibiafiles + - game_things @onLoad: | dofile 'const' diff --git a/modules/gamelib/protocollogin.lua b/modules/gamelib/protocollogin.lua index 4bf513c2..28adc3a6 100644 --- a/modules/gamelib/protocollogin.lua +++ b/modules/gamelib/protocollogin.lua @@ -28,6 +28,7 @@ end function ProtocolLogin:sendLoginPacket() local msg = OutputMessage.create() + msg:addU8(ClientOpcodes.ClientEnterAccount) msg:addU16(g_game.getOs()) diff --git a/src/client/mapio.cpp b/src/client/mapio.cpp index d66fb89c..7ea720ba 100644 --- a/src/client/mapio.cpp +++ b/src/client/mapio.cpp @@ -166,7 +166,7 @@ void Map::loadOtbm(const std::string& fileName, const UIWidgetPtr& pbar) } if(house && item->isMoveable()) { - g_logger.warning(stdext::format("Movable item found in house: %d at pos %s - escaping...", item->getId(), stdext::to_string(pos))); + g_logger.warning(stdext::format("Moveable item found in house: %d at pos %s - escaping...", item->getId(), stdext::to_string(pos))); item.reset(); } diff --git a/src/client/minimap.cpp b/src/client/minimap.cpp index 63b6282f..bfa73e0a 100644 --- a/src/client/minimap.cpp +++ b/src/client/minimap.cpp @@ -79,13 +79,13 @@ void MinimapBlock::update() void MinimapBlock::updateTile(int x, int y, const MinimapTile& tile) { - if(!(m_tiles[getTileIndex(x,y)] == tile)) { - m_tiles[getTileIndex(x,y)] = tile; + if(tile.color != 0) + m_shouldDraw = true; - if(tile.color != 0) - m_shouldDraw = true; + if(m_tiles[getTileIndex(x,y)].color != tile.color) m_mustUpdate = true; - } + + m_tiles[getTileIndex(x,y)] = tile; } void Minimap::init() diff --git a/src/client/protocolcodes.h b/src/client/protocolcodes.h index 80df5c96..43c8e9b3 100644 --- a/src/client/protocolcodes.h +++ b/src/client/protocolcodes.h @@ -42,9 +42,9 @@ namespace Proto { enum GameServerOpcodes : uint8 { - GameServerLoginOrPendingState = 10, + GameServerLoginOrPendingState = 10, GameServerGMActions = 11, - GameServerEnterGame = 15, + GameServerEnterGame = 15, GameServerUpdateNeeded = 17, GameServerLoginError = 20, GameServerLoginAdvice = 21, diff --git a/src/client/protocolgame.h b/src/client/protocolgame.h index f357450a..0c15ad76 100644 --- a/src/client/protocolgame.h +++ b/src/client/protocolgame.h @@ -107,6 +107,8 @@ public: void sendNewNewRuleViolation(int reason, int action, const std::string& characterName, const std::string& comment, const std::string& translation); void sendRequestItemInfo(int itemId, int subType, int index); void sendAnswerModalDialog(int dialog, int button, int choice); + + // otclient only void sendChangeMapAwareRange(int xrange, int yrange); protected: