Browse Source

a lot of changes in modules

Eduardo Bart 9 years ago
parent
commit
88301c329a
50 changed files with 488 additions and 318 deletions
  1. 3
    0
      addons/.gitignore
  2. 1
    0
      addons/README.txt
  3. 0
    24
      modules/client/client.otmod
  4. 6
    0
      modules/client_about/about.otmod
  5. 2
    0
      modules/client_background/background.otmod
  6. 5
    5
      modules/client_entergame/characterlist.lua
  7. 18
    9
      modules/client_entergame/entergame.lua
  8. 2
    0
      modules/client_entergame/entergame.otmod
  9. 0
    1
      modules/client_main/client.lua
  10. 14
    0
      modules/client_main/client.otmod
  11. 0
    0
      modules/client_main/clienticon.png
  12. 39
    7
      modules/client_modulemanager/modulemanager.lua
  13. 6
    0
      modules/client_modulemanager/modulemanager.otmod
  14. 31
    15
      modules/client_modulemanager/modulemanager.otui
  15. 5
    0
      modules/client_options/options.otmod
  16. 0
    8
      modules/client_terminal/commands.lua
  17. 4
    1
      modules/client_terminal/terminal.lua
  18. 3
    4
      modules/client_terminal/terminal.otmod
  19. 0
    0
      modules/client_terminal/terminal.otui
  20. 0
    0
      modules/client_terminal/terminal.png
  21. 6
    3
      modules/client_topmenu/topmenu.lua
  22. 2
    0
      modules/client_topmenu/topmenu.otmod
  23. 0
    17
      modules/core/core.otmod
  24. 2
    1
      modules/core_fonts/core_fonts.otmod
  25. 2
    2
      modules/core_lib/core_lib.otmod
  26. 13
    27
      modules/core_lib/dispatcher.lua
  27. 13
    0
      modules/core_lib/globals.lua
  28. 22
    0
      modules/core_lib/util.lua
  29. 4
    0
      modules/core_styles/core_styles.otmod
  30. 19
    0
      modules/core_styles/styles/messagebox.otui
  31. 1
    17
      modules/core_styles/styles/windows.otui
  32. 13
    3
      modules/core_widgets/core_widgets.otmod
  33. 0
    79
      modules/core_widgets/messagebox/messagebox.lua
  34. 0
    21
      modules/core_widgets/messagebox/messagebox.otui
  35. 35
    22
      modules/core_widgets/tooltip.lua
  36. 0
    7
      modules/core_widgets/tooltip/tooltip.otui
  37. 51
    10
      modules/core_widgets/uimessagebox.lua
  38. 2
    2
      modules/game/game.lua
  39. 0
    1
      modules/game/game.otui
  40. 1
    1
      src/framework/core/eventdispatcher.h
  41. 30
    4
      src/framework/core/module.cpp
  42. 7
    0
      src/framework/core/module.h
  43. 54
    7
      src/framework/core/modulemanager.cpp
  44. 8
    2
      src/framework/core/modulemanager.h
  45. 10
    2
      src/framework/graphics/fontmanager.cpp
  46. 9
    0
      src/framework/luafunctions.cpp
  47. 19
    0
      src/framework/luascript/luavaluecasts.h
  48. 14
    11
      src/framework/ui/uiwidget.cpp
  49. 1
    1
      src/framework/ui/uiwidgettext.cpp
  50. 11
    4
      src/otclient/otclient.cpp

+ 3
- 0
addons/.gitignore View File

@@ -0,0 +1,3 @@
1
+*
2
+!/README.txt
3
+!.gitignore

+ 1
- 0
addons/README.txt View File

@@ -0,0 +1 @@
1
+You can plance any personal addons here.

+ 0
- 24
modules/client/client.otmod View File

@@ -1,24 +0,0 @@
1
-Module
2
-  name: client
3
-  description: Load all other otclient dependecies
4
-  author: OTClient team
5
-  website: https://github.com/edubart/otclient
6
-  canUnload: false
7
-
8
-  // NOTE: order does matter
9
-  dependencies:
10
-    - client_background
11
-    - client_topmenu
12
-    - client_tibiafiles
13
-    - client_about
14
-    - client_options
15
-    - client_entergame
16
-    - client_modulemanager
17
-    - game
18
-
19
-  onLoad: |
20
-    dofile 'client'
21
-    Client.init()
22
-
23
-  onUnload: |
24
-    Client.terminate()

+ 6
- 0
modules/client_about/about.otmod View File

@@ -3,6 +3,12 @@ Module
3 3
   description: Create the about window
4 4
   author: OTClient team
5 5
   website: https://github.com/edubart/otclient
6
+  autoload: true
7
+  autoload-antecedence: 160
8
+  unloadable: true
9
+
10
+  dependencies:
11
+    - client_topmenu
6 12
 
7 13
   onLoad: |
8 14
     dofile 'about'

+ 2
- 0
modules/client_background/background.otmod View File

@@ -3,6 +3,8 @@ Module
3 3
   description: Handles the background of the login screen
4 4
   author: OTClient team
5 5
   website: https://github.com/edubart/otclient
6
+  autoload: true
7
+  autoload-antecedence: 110
6 8
 
7 9
   onLoad: |
8 10
     dofile 'background'

+ 5
- 5
modules/client_entergame/characterlist.lua View File

@@ -42,11 +42,11 @@ local function tryLogin(charInfo, tries)
42 42
   Game.loginWorld(EnterGame.account, EnterGame.password, charInfo.worldHost, charInfo.worldPort, charInfo.characterName)
43 43
 
44 44
   loadBox = displayCancelBox('Please wait', 'Connecting to game server...')
45
-  function loadBox.onCancel()
46
-    loadBox = nil
47
-    Game.cancelLogin()
48
-    CharacterList.show()
49
-  end
45
+  connect(loadBox, { onCancel = function()
46
+                                  loadBox = nil
47
+                                  Game.cancelLogin()
48
+                                  CharacterList.show()
49
+                                end })
50 50
 
51 51
   -- save last used character
52 52
   Settings.set('lastUsedCharacter', charInfo.characterName)

+ 18
- 9
modules/client_entergame/entergame.lua View File

@@ -8,7 +8,6 @@ local motdMessage
8 8
 local motdButton
9 9
 local enterGameButton
10 10
 
11
-
12 11
 -- private functions
13 12
 local function clearAccountFields()
14 13
   enterGame:getChildById('accountNameLineEdit'):clearText()
@@ -20,11 +19,13 @@ end
20 19
 
21 20
 local function onError(protocol, message, connectionError)
22 21
   loadBox:destroy()
22
+  loadBox = nil
23
+
23 24
   if not connectionError then
24 25
     clearAccountFields()
25 26
   end
26 27
   local errorBox = displayErrorBox('Login Error', message)
27
-  errorBox.onOk = EnterGame.show
28
+  connect(errorBox, { onOk = EnterGame.show })
28 29
 end
29 30
 
30 31
 local function onMotd(protocol, motd)
@@ -43,13 +44,15 @@ local function onCharacterList(protocol, characters, premDays)
43 44
   end
44 45
 
45 46
   loadBox:destroy()
47
+  loadBox = nil
48
+
46 49
   CharacterList.create(characters, premDays)
47 50
 
48 51
   local lastMotdNumber = Settings.getNumber("motd")
49 52
   if motdNumber and motdNumber ~= lastMotdNumber then
50 53
     Settings.set("motd", motdNumber)
51 54
     local motdBox = displayInfoBox("Message of the day", motdMessage)
52
-    motdBox.onOk = CharacterList.show
55
+    connect(motdBox, { onOk = CharacterList.show })
53 56
     CharacterList.hide()
54 57
   end
55 58
 end
@@ -76,8 +79,13 @@ function EnterGame.init()
76 79
   enterGame:getChildById('rememberPasswordBox'):setChecked(#account > 0)
77 80
   enterGame:getChildById('accountNameLineEdit'):focus()
78 81
 
79
-  if #account > 0 and autologin then
80
-    addEvent(EnterGame.doLogin)
82
+  -- only open entergame when app starts
83
+  if not g_app.isRunning() then
84
+    if #account > 0 and autologin then
85
+      addEvent(EnterGame.doLogin)
86
+    end
87
+  else
88
+    enterGame:hide()
81 89
   end
82 90
 end
83 91
 
@@ -124,10 +132,11 @@ function EnterGame.doLogin()
124 132
   protocolLogin.onCharacterList = onCharacterList
125 133
 
126 134
   loadBox = displayCancelBox('Please wait', 'Connecting to login server...')
127
-  loadBox.onCancel = function(msgbox)
128
-    protocolLogin:cancelLogin()
129
-    EnterGame.show()
130
-  end
135
+  connect(loadBox, { onCancel = function(msgbox)
136
+                                  loadBox = nil
137
+                                  protocolLogin:cancelLogin()
138
+                                  EnterGame.show()
139
+                                end })
131 140
 
132 141
   protocolLogin:login(EnterGame.host, EnterGame.port, EnterGame.account, EnterGame.password)
133 142
 end

+ 2
- 0
modules/client_entergame/entergame.otmod View File

@@ -3,6 +3,8 @@ Module
3 3
   description: Manages enter game and character list windows
4 4
   author: OTClient team
5 5
   website: https://github.com/edubart/otclient
6
+  autoload: true
7
+  autoload-antecedence: 150
6 8
 
7 9
   onLoad: |
8 10
     dofile 'entergame'

modules/client/client.lua → modules/client_main/client.lua View File

@@ -34,5 +34,4 @@ function Client.terminate()
34 34
   Settings.set('window-size', g_window.getUnmaximizedSize())
35 35
   Settings.set('window-pos', g_window.getUnmaximizedPos())
36 36
   Settings.set('window-maximized', g_window.isMaximized())
37
-  g_window.hide()
38 37
 end

+ 14
- 0
modules/client_main/client.otmod View File

@@ -0,0 +1,14 @@
1
+Module
2
+  name: client_main
3
+  description: Initialize the client and setups its main window
4
+  author: OTClient team
5
+  website: https://github.com/edubart/otclient
6
+  autoload: true
7
+  autoload-antecedence: 100
8
+
9
+  onLoad: |
10
+    dofile 'client'
11
+    Client.init()
12
+
13
+  onUnload: |
14
+    Client.terminate()

modules/client/clienticon.png → modules/client_main/clienticon.png View File


+ 39
- 7
modules/client_modulemanager/modulemanager.lua View File

@@ -54,25 +54,40 @@ function ModuleManager.refreshModules()
54 54
 end
55 55
 
56 56
 function ModuleManager.listModules()
57
+  if not moduleManagerWindow then return end
58
+
57 59
   moduleList:destroyChildren()
58 60
 
59 61
   local modules = g_modules.getModules()
60 62
   for i,module in ipairs(modules) do
61 63
     local label = createWidget('ModuleListLabel', moduleList)
62 64
     label:setText(module:getName())
65
+    label:setOn(module:isLoaded())
63 66
   end
64 67
 
65 68
   moduleList:focusChild(moduleList:getFirstChild(), ActiveFocusReason)
66 69
 end
67 70
 
71
+function ModuleManager.refreshLoadedModules()
72
+  if not moduleManagerWindow then return end
73
+
74
+  for i,child in ipairs(moduleList:getChildren()) do
75
+    local module = g_modules.getModule(child:getText())
76
+    child:setOn(module:isLoaded())
77
+  end
78
+end
79
+
68 80
 function ModuleManager.updateModuleInfo(moduleName)
81
+  if not moduleManagerWindow then return end
82
+
69 83
   local name = ''
70 84
   local description = ''
71 85
   local autoLoad = ''
72 86
   local author = ''
73 87
   local website = ''
74 88
   local version = ''
75
-  local canLoad = false
89
+  local loaded = false
90
+  local canReload = false
76 91
   local canUnload = false
77 92
 
78 93
   local module = g_modules.getModule(moduleName)
@@ -82,8 +97,9 @@ function ModuleManager.updateModuleInfo(moduleName)
82 97
     author = module:getAuthor()
83 98
     website = module:getWebsite()
84 99
     version = module:getVersion()
85
-    canUnload = module:isLoaded()
86
-    canLoad = not canUnload
100
+    loaded = module:isLoaded()
101
+    canUnload = module:canUnload()
102
+    canReload = not loaded or canUnload
87 103
   end
88 104
 
89 105
   moduleManagerWindow:recursiveGetChildById('moduleName'):setText(name)
@@ -93,17 +109,26 @@ function ModuleManager.updateModuleInfo(moduleName)
93 109
   moduleManagerWindow:recursiveGetChildById('moduleWebsite'):setText(website)
94 110
   moduleManagerWindow:recursiveGetChildById('moduleVersion'):setText(version)
95 111
 
96
-  moduleManagerWindow:recursiveGetChildById('moduleLoadButton'):setEnabled(canLoad)
97
-  moduleManagerWindow:recursiveGetChildById('moduleUnloadButton'):setEnabled(canUnload)
112
+  local reloadButton = moduleManagerWindow:recursiveGetChildById('moduleReloadButton')
113
+  reloadButton:setEnabled(canReload)
114
+  reloadButton:setVisible(true)
115
+  if loaded then reloadButton:setText('Reload')
116
+  else reloadButton:setText('Load') end
117
+
118
+  local unloadButton = moduleManagerWindow:recursiveGetChildById('moduleUnloadButton')
119
+  unloadButton:setVisible(true)
120
+  unloadButton:setEnabled(canUnload)
98 121
 end
99 122
 
100
-function ModuleManager.loadCurrentModule()
123
+function ModuleManager.reloadCurrentModule()
101 124
   local focusedChild = moduleList:getFocusedChild()
102 125
   if focusedChild then
103 126
     local module = g_modules.getModule(focusedChild:getText())
104 127
     if module then
105
-      module:load()
128
+      module:reload()
106 129
       ModuleManager.updateModuleInfo(module:getName())
130
+      ModuleManager.refreshLoadedModules()
131
+      ModuleManager.show()
107 132
     end
108 133
   end
109 134
 end
@@ -115,7 +140,14 @@ function ModuleManager.unloadCurrentModule()
115 140
     if module then
116 141
       module:unload()
117 142
       ModuleManager.updateModuleInfo(module:getName())
143
+      ModuleManager.refreshLoadedModules()
118 144
     end
119 145
   end
120 146
 end
121 147
 
148
+function ModuleManager.reloadAllModules()
149
+  g_modules.reloadModules()
150
+  ModuleManager.refreshLoadedModules()
151
+  ModuleManager.show()
152
+end
153
+

+ 6
- 0
modules/client_modulemanager/modulemanager.otmod View File

@@ -3,6 +3,12 @@ Module
3 3
   description: Manage other modules
4 4
   author: OTClient team
5 5
   website: https://github.com/edubart/otclient
6
+  autoload: true
7
+  autoload-antecedence: 140
8
+
9
+  dependencies:
10
+    - client_topmenu
11
+
6 12
   onLoad: |
7 13
     dofile 'modulemanager'
8 14
     ModuleManager.init()

+ 31
- 15
modules/client_modulemanager/modulemanager.otui View File

@@ -3,11 +3,21 @@ ModuleListLabel < Label
3 3
   background-color: alpha
4 4
   text-offset: 2 0
5 5
   focusable: true
6
+  color: #cccccc
6 7
 
7 8
   $focus:
8
-    background-color: #ffffff22
9 9
     color: #ffffff
10 10
 
11
+  $on:
12
+    background-color: #006600
13
+  $!on:
14
+    background-color: #660000
15
+
16
+  $on focus:
17
+    background-color: #004400
18
+  $!on focus:
19
+    background-color: #440000
20
+
11 21
 ModuleInfoLabel < Label
12 22
   $!first:
13 23
     margin-top: 5
@@ -19,10 +29,11 @@ ModuleValueLabel < UILabel
19 29
   text-offset: 3 0
20 30
   image-source: /core_styles/images/panel_flat.png
21 31
   image-border: 1
32
+  height: 16
22 33
 
23 34
 MainWindow
24 35
   id: moduleManagerWindow
25
-  size: 450 450
36
+  size: 480 450
26 37
   text: Module Manager
27 38
 
28 39
   @onEscape: ModuleManager.hide()
@@ -38,21 +49,31 @@ MainWindow
38 49
     margin-bottom: 30
39 50
 
40 51
   Button
52
+    id: refreshModulesButton
41 53
     anchors.top: moduleList.bottom
42
-    anchors.horizontalCenter: moduleList.horizontalCenter
54
+    anchors.left: moduleList.left
43 55
     margin-top: 8
56
+    width: 80
44 57
     text: Refresh
45 58
     @onClick: ModuleManager.refreshModules()
46 59
 
60
+  Button
61
+    id: reloadAllModulesButton
62
+    anchors.top: moduleList.bottom
63
+    anchors.right: moduleList.right
64
+    margin-top: 8
65
+    width: 80
66
+    text: Reload All
67
+    @onClick: ModuleManager.reloadAllModules()
68
+
47 69
   Panel
48 70
     id: moduleInfo
49 71
     anchors.left: moduleList.right
50 72
     anchors.top: parent.top
51 73
     anchors.right: parent.right
52 74
     margin: 0 5 5 15
53
-    layout:
54
-      type: verticalBox
55
-      fit-children: true
75
+    layout: verticalBox
76
+    height: 265
56 77
 
57 78
     ModuleInfoLabel
58 79
       text: Module name
@@ -65,11 +86,6 @@ MainWindow
65 86
       id: moduleDescription
66 87
       height: 100
67 88
 
68
-    ModuleInfoLabel
69
-      text: Loaded
70
-    ModuleValueLabel
71
-      id: moduleLoaded
72
-
73 89
     //ModuleInfoLabel
74 90
     //  text: Autoload
75 91
     //ModuleValueLabel
@@ -97,13 +113,13 @@ MainWindow
97 113
       id: moduleVersion
98 114
 
99 115
   Button
100
-    id: moduleLoadButton
116
+    id: moduleReloadButton
101 117
     anchors.top: moduleInfo.bottom
102 118
     anchors.left: moduleInfo.left
103 119
     margin-top: 8
104 120
     text: Load
105
-    enabled: false
106
-    @onClick: ModuleManager.loadCurrentModule()
121
+    visible: false
122
+    @onClick: ModuleManager.reloadCurrentModule()
107 123
 
108 124
   Button
109 125
     id: moduleUnloadButton
@@ -112,6 +128,6 @@ MainWindow
112 128
     margin-left: 10
113 129
     margin-top: 8
114 130
     text: Unload
115
-    enabled: false
131
+    visible: false
116 132
     @onClick: ModuleManager.unloadCurrentModule()
117 133
 

+ 5
- 0
modules/client_options/options.otmod View File

@@ -3,6 +3,11 @@ Module
3 3
   description: Create the options window
4 4
   author: OTClient team
5 5
   website: https://github.com/edubart/otclient
6
+  autoload: true
7
+  autoload-antecedence: 130
8
+
9
+  dependencies:
10
+    - client_topmenu
6 11
 
7 12
   onLoad: |
8 13
     dofile 'options'

modules/addon_terminal/commands.lua → modules/client_terminal/commands.lua View File

@@ -45,14 +45,6 @@ function quit()
45 45
   exit()
46 46
 end
47 47
 
48
-function reloadModule(name)
49
-  local module = g_modules.getModule(name)
50
-  if module then
51
-    module:unload()
52
-    module:load()
53
-  end
54
-end
55
-
56 48
 function autoReloadModule(name)
57 49
   local function reloadEvent()
58 50
     reloadModule(name)

modules/addon_terminal/terminal.lua → modules/client_terminal/terminal.lua View File

@@ -86,7 +86,10 @@ end
86 86
 local function doCommand()
87 87
   local currentCommand = commandLineEdit:getText()
88 88
   Terminal.executeCommand(currentCommand)
89
-  commandLineEdit:clearText()
89
+
90
+  if commandLineEdit then
91
+    commandLineEdit:clearText()
92
+  end
90 93
   return true
91 94
 end
92 95
 

modules/addon_terminal/terminal.otmod → modules/client_terminal/terminal.otmod View File

@@ -1,11 +1,10 @@
1 1
 Module
2
-  name: terminal
2
+  name: client_terminal
3 3
   description: Terminal for executing lua functions
4 4
   author: OTClient team
5 5
   website: https://github.com/edubart/otclient
6
-
7
-  autoLoad: true
8
-  autoLoadAntecedence: 200
6
+  autoload: true
7
+  autoload-antecedence: 160
9 8
 
10 9
   onLoad: |
11 10
     dofile 'terminal'

modules/addon_terminal/terminal.otui → modules/client_terminal/terminal.otui View File


modules/addon_terminal/terminal.png → modules/client_terminal/terminal.png View File


+ 6
- 3
modules/client_topmenu/topmenu.lua View File

@@ -24,6 +24,9 @@ function TopMenu.init()
24 24
 
25 25
   TopMenu.addRightButton('logoutButton', 'Logout (Ctrl+Q)', '/core_styles/icons/logout.png', onLogout)
26 26
   Keyboard.bindKeyDown('Ctrl+Q', onLogout)
27
+
28
+  connect(Game, { onLogin = TopMenu.showGameButtons,
29
+                  onLogout = TopMenu.hideGameButtons })
27 30
 end
28 31
 
29 32
 function TopMenu.terminate()
@@ -33,6 +36,9 @@ function TopMenu.terminate()
33 36
   gameButtonsPanel = nil
34 37
   topMenu:destroy()
35 38
   topMenu = nil
39
+
40
+  disconnect(Game, { onLogin = TopMenu.showGameButtons,
41
+                     onLogout = TopMenu.hideGameButtons })
36 42
 end
37 43
 
38 44
 function TopMenu.addButton(id, description, icon, callback, right)
@@ -82,6 +88,3 @@ end
82 88
 function TopMenu.getButton(id)
83 89
   return topMenu:recursiveGetChildById(id)
84 90
 end
85
-
86
-connect(Game, { onLogin = TopMenu.showGameButtons,
87
-                onLogout = TopMenu.hideGameButtons })

+ 2
- 0
modules/client_topmenu/topmenu.otmod View File

@@ -3,6 +3,8 @@ Module
3 3
   description: Create the top menu
4 4
   author: OTClient team
5 5
   website: https://github.com/edubart/otclient
6
+  autoload: true
7
+  autoload-antecedence: 120
6 8
 
7 9
   onLoad: |
8 10
     dofile 'topmenu'

+ 0
- 17
modules/core/core.otmod View File

@@ -1,17 +0,0 @@
1
-Module
2
-  name: core
3
-  description: Contains lua classes, functions and constants used by other modules
4
-  author: OTClient team
5
-  website: https://github.com/edubart/otclient
6
-
7
-  // core must be loaded before other modules
8
-  autoLoad: true
9
-  autoLoadAntecedence: 10
10
-
11
-  // NOTE: order does matter
12
-  dependencies:
13
-    - core_lib
14
-    - core_fonts
15
-    - core_styles
16
-    - core_widgets
17
-

+ 2
- 1
modules/core_fonts/core_fonts.otmod View File

@@ -3,7 +3,8 @@ Module
3 3
   description: Contains fonts used by core
4 4
   author: OTClient team
5 5
   website: https://github.com/edubart/otclient
6
-  canUnload: false
6
+  autoload: true
7
+  autoload-antecedence: 30
7 8
 
8 9
   onLoad: |
9 10
     importFont 'verdana-11px-antialised'

+ 2
- 2
modules/core_lib/core_lib.otmod View File

@@ -3,6 +3,8 @@ Module
3 3
   description: Contains core lua classes, functions and constants used by other modules
4 4
   author: OTClient team
5 5
   website: https://github.com/edubart/otclient
6
+  autoload: true
7
+  autoload-antecedence: 10
6 8
 
7 9
   onLoad: |
8 10
     dofile 'ext/table'
@@ -21,5 +23,3 @@ Module
21 23
     dofile 'keyboard'
22 24
     dofile 'mouse'
23 25
 
24
-  onUnload: |
25
-    rootWidget = nil

+ 13
- 27
modules/core_lib/dispatcher.lua View File

@@ -1,34 +1,20 @@
1
-local eventId = 0
2
-local eventList = {}
3
-
4 1
 function scheduleEvent(func, delay)
5
-  if not func then return end
6
-  eventId = eventId + 1
7
-  local id = eventId
8
-  local function proxyFunc()
9
-    if eventList[id] then
10
-      if eventList[id].active then
11
-        func()
12
-      end
13
-      eventList[id] = nil
14
-    end
15
-  end
16
-  eventList[id] = { func = proxyFunc, active = true }
17
-  if delay and delay > 0 then
18
-    g_dispatcher.scheduleEvent(proxyFunc, delay)
19
-  else
20
-    g_dispatcher.addEvent(proxyFunc, false)
21
-  end
22
-  return id
2
+  local event = g_dispatcher.scheduleEvent(callback, delay)
3
+
4
+  -- must hold a reference to the callback, otherwise it would be collected
5
+  event._callback = callback
6
+  return event
23 7
 end
24 8
 
25
-function addEvent(func)
26
-  return scheduleEvent(func, 0)
9
+function addEvent(callback, front)
10
+  local event = g_dispatcher.addEvent(callback, front)
11
+  -- must hold a reference to the callback, otherwise it would be collected
12
+  event._callback = callback
13
+  return event
27 14
 end
28 15
 
29
-function removeEvent(id)
30
-  if id and eventList[id] then
31
-    eventList[id].active = false
32
-    return true
16
+function removeEvent(event)
17
+  if event then
18
+    event:cancel()
33 19
   end
34 20
 end

+ 13
- 0
modules/core_lib/globals.lua View File

@@ -74,3 +74,16 @@ function createWidget(style, parent)
74 74
   widget:setStyle(style)
75 75
   return widget
76 76
 end
77
+
78
+function reloadModule(name)
79
+  local module = g_modules.getModule(name)
80
+  if module then
81
+    module:reload()
82
+  end
83
+end
84
+
85
+function reloadModules()
86
+  g_modules.reloadModules()
87
+end
88
+
89
+

+ 22
- 0
modules/core_lib/util.lua View File

@@ -35,6 +35,28 @@ function connect(object, signalsAndSlots, pushFront)
35 35
   end
36 36
 end
37 37
 
38
+function disconnect(object, signalsAndSlots)
39
+  for signal,slot in pairs(signalsAndSlots) do
40
+    if not object[signal] then
41
+    elseif type(object[signal]) == 'function' then
42
+      if object[signal] == slot then
43
+        object[signal] = nil
44
+      end
45
+    elseif type(object[signal]) == 'table' then
46
+      for k,func in pairs(object[signal]) do
47
+        if func == slot then
48
+          table.remove(object[signal], k)
49
+
50
+          if #object[signal] == 1 then
51
+            object[signal] = object[signal][1]
52
+          end
53
+          break
54
+        end
55
+      end
56
+    end
57
+  end
58
+end
59
+
38 60
 function extends(base)
39 61
   local derived = {}
40 62
   function derived.internalCreate()

+ 4
- 0
modules/core_styles/core_styles.otmod View File

@@ -3,6 +3,9 @@ Module
3 3
   description: Contains ui styles used by other modules
4 4
   author: OTClient team
5 5
   website: https://github.com/edubart/otclient
6
+  reloadable: true
7
+  autoload: true
8
+  autoload-antecedence: 20
6 9
 
7 10
   onLoad: |
8 11
     importStyle 'styles/buttons.otui'
@@ -20,3 +23,4 @@ Module
20 23
     importStyle 'styles/popupmenus.otui'
21 24
     importStyle 'styles/comboboxes.otui'
22 25
     importStyle 'styles/spinboxes.otui'
26
+    importStyle 'styles/messagebox.otui'

+ 19
- 0
modules/core_styles/styles/messagebox.otui View File

@@ -0,0 +1,19 @@
1
+MessageBoxWindow < MainWindow
2
+  id: messageBoxWindow
3
+  anchors.centerIn: parent
4
+  height: 60
5
+  width: 80
6
+  padding-bottom: 10
7
+  padding-right: 10
8
+
9
+MessageBoxLabel < Label
10
+  id: messageBoxLabel
11
+  anchors.horizontalCenter: parent.horizontalCenter
12
+  anchors.top: parent.top
13
+
14
+MessageBoxRightButton < Button
15
+  id: messageBoxRightButton
16
+  anchors.bottom: parent.bottom
17
+  anchors.right: parent.right
18
+  width: 64
19
+  visible: true

+ 1
- 17
modules/core_styles/styles/windows.otui View File

@@ -1,20 +1,4 @@
1 1
 Window < UIWindow
2
-  font: verdana-11px-antialised
3
-  size: 200 200
4
-  opacity: 1
5
-  color: white
6
-  text-offset: 0 2
7
-  text-align: top
8
-  move-policy: free
9
-  stackable: true
10
-  image-source: /core_styles/images/window.png
11
-  image-border: 4
12
-  image-border-top: 20
13
-
14
-  $disabled:
15
-    color: #aaaaaa88
16
-
17
-Window2 < UIWindow
18 2
   font: verdana-11px-antialised
19 3
   size: 200 200
20 4
   opacity: 1
@@ -31,7 +15,7 @@ Window2 < UIWindow
31 15
   $disabled:
32 16
     color: #aaaaaa88
33 17
 
34
-MainWindow < Window2
18
+MainWindow < Window
35 19
   anchors.centerIn: parent
36 20
 
37 21
 MiniWindow < UIWindow

+ 13
- 3
modules/core_widgets/core_widgets.otmod View File

@@ -3,6 +3,10 @@ Module
3 3
   description: Contains widgets used by other modules
4 4
   author: OTClient team
5 5
   website: https://github.com/edubart/otclient
6
+  reloadable: true
7
+  unloadble: false
8
+  autoload: true
9
+  autoload-antecedence: 40
6 10
 
7 11
   onLoad: |
8 12
     dofile 'uiwidget'
@@ -16,6 +20,12 @@ Module
16 20
     dofile 'uipopupmenu'
17 21
     dofile 'uiwindow'
18 22
     dofile 'uiitem'
19
-    dofile 'tooltip/tooltip'
20
-    dofile 'messagebox/messagebox'
21
-    
23
+    dofile 'uimessagebox'
24
+
25
+    dofile 'tooltip'
26
+    --dofile 'messagebox/messagebox'
27
+
28
+    ToolTip.init()
29
+
30
+  onUnload: |
31
+    ToolTip.terminate()

+ 0
- 79
modules/core_widgets/messagebox/messagebox.lua View File

@@ -1,81 +0,0 @@
1
-MessageBox = {}
2
-MessageBox.__index = MessageBox
3
-
4
-MessageBoxOk = 1
5
-MessageBoxCancel = 2
6
-
7
-function MessageBox.create(title, text, flags)
8
-  local box = {}
9
-  setmetatable(box, MessageBox)
10
-
11
-  -- create messagebox window
12
-  local window = displayUI('messagebox.otui')
13
-  window:lock()
14
-  window:setText(title)
15
-
16
-  local label = window:getChildById('messageBoxLabel')
17
-  label:setText(text)
18
-  label:resizeToText()
19
-
20
-  -- set window size based on label size
21
-  window:setWidth(math.max(label:getWidth() + 48, 120))
22
-  window:setHeight(label:getHeight() + 64)
23
-
24
-  -- setup messagebox first button
25
-  local buttonRight = window:getChildById('messageBoxRightButton')
26
-
27
-  if flags == MessageBoxOk then
28
-    buttonRight:setText("Ok")
29
-    box.onOk = function() end
30
-    buttonRight.onClick = function()
31
-      box.onOk()
32
-      box:destroy()
33
-    end
34
-    window.onEnter = buttonRight.onClick
35
-    window.onEscape = buttonRight.onClick
36
-  elseif flags == MessageBoxCancel then
37
-    buttonRight:setText("Cancel")
38
-    box.onCancel = function() end
39
-    buttonRight.onClick = function()
40
-      box.onCancel()
41
-      box:destroy()
42
-    end
43
-    window.onEnter = buttonRight.onClick
44
-    window.onEscape = buttonRight.onClick
45
-  end
46
-
47
-  box.window = window
48
-  return box
49
-end
50
-
51
-function MessageBox:destroy()
52
-  if self.onDestroy then
53
-    self.onDestroy()
54
-    self.onDestroy = nil
55
-  end
56
-  if self.window then
57
-    self.window:destroy()
58
-    self.window = nil
59
-  end
60
-  self.onOk = nil
61
-  self.onCancel = nil
62
-end
63
-
64
-function displayMessageBox(title, text, flags)
65
-  return MessageBox.create(title, text, flags)
66
-end
67
-
68
-function displayErrorBox(title, text)
69
-  return MessageBox.create(title, text, MessageBoxOk)
70
-end
71
-
72
-function displayInfoBox(title, text)
73
-  return MessageBox.create(title, text, MessageBoxOk)
74
-end
75
-
76
-function displayCancelBox(title, text)
77
-  return MessageBox.create(title, text, MessageBoxCancel)
78
-end
79
-

+ 0
- 21
modules/core_widgets/messagebox/messagebox.otui View File

@@ -1,21 +0,0 @@
1
-Window
2
-  id: messageBoxWindow
3
-  anchors.centerIn: parent
4
-  height: 80
5
-  width: 120
6
-
7
-  Label
8
-    id: messageBoxLabel
9
-    anchors.horizontalCenter: parent.horizontalCenter
10
-    anchors.top: parent.top
11
-    margin-top: 27
12
-    margin-bottom : 27
13
-
14
-  Button
15
-    id: messageBoxRightButton
16
-    anchors.bottom: parent.bottom
17
-    anchors.right: parent.right
18
-    margin-right: 10
19
-    margin-bottom: 10
20
-    width: 64
21
-    visible: true

modules/core_widgets/tooltip/tooltip.lua → modules/core_widgets/tooltip.lua View File

@@ -6,6 +6,8 @@ local currentHoveredWidget
6 6
 
7 7
 -- private functions
8 8
 local function moveToolTip(tooltip)
9
+  if not tooltip:isVisible() then return end
10
+
9 11
   local pos = g_window.getMousePosition()
10 12
   pos.y = pos.y + 1
11 13
   local xdif = g_window.getSize().width - (pos.x + tooltip:getWidth())
@@ -17,28 +19,6 @@ local function moveToolTip(tooltip)
17 19
   tooltip:setPosition(pos)
18 20
 end
19 21
 
20
-function ToolTip.display(text)
21
-  if text == nil then return end
22
-  ToolTip.hide()
23
-  toolTipLabel = createWidget('Label', rootWidget)
24
-  toolTipLabel:setId('toolTip')
25
-  toolTipLabel:setBackgroundColor('#111111bb')
26
-  toolTipLabel:setText(text)
27
-  toolTipLabel:resizeToText()
28
-  toolTipLabel:resize(toolTipLabel:getWidth() + 4, toolTipLabel:getHeight() + 4)
29
-  toolTipLabel.onMouseMove = moveToolTip
30
-  moveToolTip(toolTipLabel)
31
-end
32
-
33
-function ToolTip.hide()
34
-  if toolTipLabel then
35
-    toolTipLabel:destroy()
36
-    toolTipLabel = nil
37
-  end
38
-end
39
-
40 22
 local function onWidgetHoverChange(widget, hovered)
41 23
   if hovered then
42 24
     if widget.tooltip then
@@ -59,8 +39,39 @@ local function onWidgetStyleApply(widget, styleName, styleNode)
59 39
   end
60 40
 end
61 41
 
62
-connect(UIWidget, {  onStyleApply = onWidgetStyleApply,
63
-                     onHoverChange = onWidgetHoverChange})
42
+-- public functions
43
+function ToolTip.init()
44
+  toolTipLabel = createWidget('Label', rootWidget)
45
+  toolTipLabel:setId('toolTip')
46
+  toolTipLabel:setBackgroundColor('#111111bb')
47
+  connect(toolTipLabel, { onMouseMove = moveToolTip })
48
+
49
+  connect(UIWidget, {  onStyleApply = onWidgetStyleApply,
50
+                       onHoverChange = onWidgetHoverChange})
51
+end
52
+
53
+function ToolTip.terminate()
54
+  disconnect(UIWidget, { onStyleApply = onWidgetStyleApply,
55
+                         onHoverChange = onWidgetHoverChange })
56
+
57
+  currentHoveredWidget = nil
58
+  toolTipLabel:destroy()
59
+  toolTipLabel = nil
60
+end
61
+
62
+function ToolTip.display(text)
63
+  if text == nil then return end
64
+  toolTipLabel:setText(text)
65
+  toolTipLabel:resizeToText()
66
+  toolTipLabel:resize(toolTipLabel:getWidth() + 4, toolTipLabel:getHeight() + 4)
67
+  toolTipLabel:show()
68
+  toolTipLabel:raise()
69
+  moveToolTip(toolTipLabel)
70
+end
71
+
72
+function ToolTip.hide()
73
+  toolTipLabel:hide()
74
+end
64 75
 
65 76
 -- UIWidget extensions
66 77
 function UIWidget:setTooltip(text)

+ 0
- 7
modules/core_widgets/tooltip/tooltip.otui View File

@@ -1,7 +0,0 @@
1
-Label
2
-  id: toolTipText
3
-  background-color: #111111bb
4
-  size: 200 200
5
-  id: toolTip
6
-  focusable: false
7
-  anchors.centerIn: parent

+ 51
- 10
modules/core_widgets/uimessagebox.lua View File

@@ -1,23 +1,64 @@
1
---[[
2 1
 UIMessageBox = extends(UIWindow)
3 2
 
4
-function UIMessageBox.create(title, message)
3
+MessageBoxOk = 1
4
+MessageBoxCancel = 2
5
+
6
+-- messagebox cannot be created from otui files
7
+UIMessageBox.create = nil
8
+
9
+function UIMessageBox.display(title, message, flags)
5 10
   local messagebox = UIMessageBox.internalCreate()
11
+  rootWidget:addChild(messagebox)
6 12
 
13
+  messagebox:setStyle('MessageBoxWindow')
7 14
   messagebox:setText(title)
8
-  local messageLabel = self:getChildById('messageLabel')
9
-  label:setText(message)
10
-  label:resizeToText()
11 15
 
12
-  window:setWidth(math.max(label:getWidth() + self:getPaddingLeft() + self:getPaddingRight(), self:getWidth()))
13
-  window:setHeight(label:getHeight() + self:getPaddingTop() + self:getPaddingBottom())
16
+  local messageLabel = createWidget('MessageBoxLabel', messagebox)
17
+  messageLabel:setText(message)
18
+  messageLabel:resizeToText()
19
+
20
+  messagebox:setWidth(math.max(messageLabel:getWidth() + 48, messagebox:getWidth()))
21
+  messagebox:setHeight(math.max(messageLabel:getHeight() + 64, messagebox:getHeight()))
22
+
23
+  -- setup messagebox first button
24
+  local buttonRight = createWidget('MessageBoxRightButton', messagebox)
25
+
26
+  if flags == MessageBoxOk then
27
+    buttonRight:setText('Ok')
28
+    connect(buttonRight, { onClick = function(self) self:getParent():ok() end })
29
+  elseif flags == MessageBoxCancel then
30
+    buttonRight:setText('Cancel')
31
+    connect(buttonRight, { onClick = function(self) self:getParent():cancel() end })
32
+  end
33
+
34
+  connect(messagebox, { onEnter = function(self) self:destroy() end })
35
+  connect(messagebox, { onEscape = function(self) self:destroy() end })
36
+
37
+  messagebox:lock()
14 38
 
15 39
   return messagebox
16 40
 end
17 41
 
18
-function UIMessageBox:setTitle(title)
42
+function displayInfoBox(title, message)
43
+  return UIMessageBox.display(title, message, MessageBoxOk)
44
+end
45
+
46
+function displayErrorBox(title, message)
47
+  return UIMessageBox.display(title, message, MessageBoxOk)
48
+end
49
+
50
+function displayCancelBox(title, message)
51
+  return UIMessageBox.display(title, message, MessageBoxCancel)
52
+end
53
+
54
+function UIMessageBox:ok()
55
+  signalcall(self.onOk, self)
56
+  self.onOk = nil
57
+  self:destroy()
19 58
 end
20 59
 
21
-function UIMessageBox:setMessage(message)
60
+function UIMessageBox:cancel()
61
+  signalcall(self.onCancel, self)
62
+  self.onCancel = nil
63
+  self:destroy()
22 64
 end
23
-]]--

+ 2
- 2
modules/game/game.lua View File

@@ -90,13 +90,13 @@ end
90 90
 function Game.onLoginError(message)
91 91
   CharacterList.destroyLoadBox()
92 92
   local errorBox = displayErrorBox("Login Error", "Login error: " .. message)
93
-  errorBox.onOk = CharacterList.show
93
+  connect(errorBox, { onOk = CharacterList.show })
94 94
 end
95 95
 
96 96
 function Game.onConnectionError(message)
97 97
   CharacterList.destroyLoadBox()
98 98
   local errorBox = displayErrorBox("Login Error", "Connection error: " .. message)
99
-  errorBox.onOk = CharacterList.show
99
+  connect(errorBox, { onOk = CharacterList.show })
100 100
 end
101 101
 
102 102
 local function onApplicationClose()

+ 0
- 1
modules/game/game.otui View File

@@ -30,5 +30,4 @@ UIGame
30 30
     id: mouseGrabber
31 31
     focusable: false
32 32
     visible: false
33
-    phantom: true
34 33
 

+ 1
- 1
src/framework/core/eventdispatcher.h View File

@@ -33,7 +33,7 @@ public:
33 33
     Event(const SimpleCallback& callback) : m_callback(callback), m_canceled(false), m_executed(false) { }
34 34
 
35 35
     void execute() {
36
-        if(!m_canceled && !m_executed) {
36
+        if(!m_canceled && !m_executed && m_callback) {
37 37
             m_callback();
38 38
             m_executed = true;
39 39
         }

+ 30
- 4
src/framework/core/module.cpp View File

@@ -52,8 +52,10 @@ bool Module::load()
52 52
     if(m_loadCallback)
53 53
         m_loadCallback();
54 54
 
55
-    logInfo("Loaded module '", m_name, "'");
56 55
     m_loaded = true;
56
+    logInfo("Loaded module '", m_name, "'");
57
+    g_modules.updateModuleLoadOrder(asModule());
58
+
57 59
     return true;
58 60
 }
59 61
 
@@ -63,9 +65,33 @@ void Module::unload()
63 65
         if(m_unloadCallback)
64 66
             m_unloadCallback();
65 67
         m_loaded = false;
68
+        logInfo("Unloaded module '", m_name, "'");
69
+        g_modules.updateModuleLoadOrder(asModule());
66 70
     }
67 71
 }
68 72
 
73
+bool Module::reload()
74
+{
75
+    unload();
76
+    return load();
77
+}
78
+
79
+bool Module::isDependent()
80
+{
81
+    for(const ModulePtr& module : g_modules.getModules()) {
82
+        if(module->isLoaded() && module->hasDependency(m_name))
83
+            return true;
84
+    }
85
+    return false;
86
+}
87
+
88
+bool Module::hasDependency(const std::string& name)
89
+{
90
+    if(std::find(m_dependencies.begin(), m_dependencies.end(), name) != m_dependencies.end())
91
+        return true;
92
+    return false;
93
+}
94
+
69 95
 void Module::discover(const OTMLNodePtr& moduleNode)
70 96
 {
71 97
     const static std::string none = "none";
@@ -73,8 +99,9 @@ void Module::discover(const OTMLNodePtr& moduleNode)
73 99
     m_author = moduleNode->valueAt("author", none);
74 100
     m_website = moduleNode->valueAt("website", none);
75 101
     m_version = moduleNode->valueAt("version", none);
76
-    m_autoLoad = moduleNode->valueAt<bool>("autoLoad", false);
77
-    m_autoLoadAntecedence = moduleNode->valueAt<int>("autoLoadAntecedence", 100);
102
+    m_autoLoad = moduleNode->valueAt<bool>("autoload", false);
103
+    m_unloadable = moduleNode->valueAt<bool>("unloadable", true);
104
+    m_autoLoadAntecedence = moduleNode->valueAt<int>("autoload-antecedence", 9999);
78 105
 
79 106
     if(OTMLNodePtr node = moduleNode->get("dependencies")) {
80 107
         for(const OTMLNodePtr& tmp : node->children())
@@ -95,4 +122,3 @@ void Module::discover(const OTMLNodePtr& moduleNode)
95 122
         m_unloadCallback = g_lua.polymorphicPop<SimpleCallback>();
96 123
     }
97 124
 }
98
-

+ 7
- 0
src/framework/core/module.h View File

@@ -35,8 +35,12 @@ public:
35 35
 
36 36
     bool load();
37 37
     void unload();
38
+    bool reload();
38 39
 
40
+    bool canUnload() { return m_loaded && m_unloadable && !isDependent(); }
39 41
     bool isLoaded() { return m_loaded; }
42
+    bool isDependent();
43
+    bool hasDependency(const std::string& name);
40 44
 
41 45
     std::string getDescription() { return m_description; }
42 46
     std::string getName() { return m_name; }
@@ -46,6 +50,8 @@ public:
46 50
     bool isAutoLoad() { return m_autoLoad; }
47 51
     int getAutoLoadAntecedence() { return m_autoLoadAntecedence; }
48 52
 
53
+    ModulePtr asModule() { return std::static_pointer_cast<Module>(shared_from_this()); }
54
+
49 55
 protected:
50 56
     void discover(const OTMLNodePtr& moduleNode);
51 57
     friend class ModuleManager;
@@ -53,6 +59,7 @@ protected:
53 59
 private:
54 60
     Boolean<false> m_loaded;
55 61
     Boolean<false> m_autoLoad;
62
+    Boolean<true> m_unloadable;
56 63
     int m_autoLoadAntecedence;
57 64
     std::string m_name;
58 65
     std::string m_description;

+ 54
- 7
src/framework/core/modulemanager.cpp View File

@@ -61,13 +61,13 @@ void ModuleManager::autoLoadModules(int maxAntecedence)
61 61
 void ModuleManager::discoverModulesPath()
62 62
 {
63 63
     // search for modules directory
64
-    std::string possibleDirs[] = { "modules",
65
-                                    g_resources.getBaseDir() + "modules",
66
-                                    g_resources.getBaseDir() + "../modules",
67
-                                    g_resources.getBaseDir() + "../share/" + g_app->getAppName() + "/modules",
68
-                                    "" };
64
+    std::string possibleModulesDirs[] = { "modules",
65
+                                          g_resources.getBaseDir() + "modules",
66
+                                          g_resources.getBaseDir() + "../modules",
67
+                                          g_resources.getBaseDir() + "../share/" + g_app->getAppName() + "/modules",
68
+                                           "" };
69 69
     bool found = false;
70
-    for(const std::string& dir : possibleDirs) {
70
+    for(const std::string& dir : possibleModulesDirs) {
71 71
         // try to add module directory
72 72
         if(g_resources.addToSearchPath(dir, false)) {
73 73
             logInfo("Using modules directory '", dir.c_str(), "'");
@@ -78,6 +78,21 @@ void ModuleManager::discoverModulesPath()
78 78
 
79 79
     if(!found)
80 80
         logFatal("Could not find modules directory");
81
+
82
+    // search for addons directory
83
+    std::string possibleAddonsDirs[] = { "addons",
84
+                                         g_resources.getBaseDir() + "addons",
85
+                                         g_resources.getBaseDir() + "../addons",
86
+                                         g_resources.getBaseDir() + "../addons/" + g_app->getAppName() + "/modules",
87
+                                         "" };
88
+    for(const std::string& dir : possibleAddonsDirs) {
89
+        // try to add module directory
90
+        if(g_resources.addToSearchPath(dir, false)) {
91
+            logInfo("Using addons directory '", dir.c_str(), "'");
92
+            found = true;
93
+            break;
94
+        }
95
+    }
81 96
 }
82 97
 
83 98
 ModulePtr ModuleManager::discoverModule(const std::string& moduleFile)
@@ -99,6 +114,7 @@ ModulePtr ModuleManager::discoverModule(const std::string& moduleFile)
99 114
         }
100 115
         module->discover(moduleNode);
101 116
 
117
+        // not loaded modules are always in back
102 118
         if(push)
103 119
             m_modules.push_back(module);
104 120
     } catch(Exception& e) {
@@ -116,10 +132,30 @@ void ModuleManager::ensureModuleLoaded(const std::string& moduleName)
116 132
 
117 133
 void ModuleManager::unloadModules()
118 134
 {
119
-    for(const ModulePtr& module : m_modules)
135
+    auto modulesBackup = m_modules;
136
+    for(const ModulePtr& module : modulesBackup)
120 137
         module->unload();
121 138
 }
122 139
 
140
+void ModuleManager::reloadModules()
141
+{
142
+    std::deque<ModulePtr> toLoadList;
143
+
144
+    // unload in the reverse direction, try to unload upto 10 times (because of dependencies)
145
+    for(int i=0;i<10;++i) {
146
+        auto modulesBackup = m_modules;
147
+        for(const ModulePtr& module : modulesBackup) {
148
+            if(module->isLoaded() && module->canUnload()) {
149
+                module->unload();
150
+                toLoadList.push_front(module);
151
+            }
152
+        }
153
+    }
154
+
155
+    for(const ModulePtr& module : toLoadList)
156
+        module->load();
157
+}
158
+
123 159
 ModulePtr ModuleManager::getModule(const std::string& moduleName)
124 160
 {
125 161
     for(const ModulePtr& module : m_modules)
@@ -127,3 +163,14 @@ ModulePtr ModuleManager::getModule(const std::string& moduleName)
127 163
             return module;
128 164
     return nullptr;
129 165
 }
166
+
167
+void ModuleManager::updateModuleLoadOrder(ModulePtr module)
168
+{
169
+    auto it = std::find(m_modules.begin(), m_modules.end(), module);
170
+    if(it != m_modules.end())
171
+        m_modules.erase(it);
172
+    if(module->isLoaded())
173
+        m_modules.push_front(module);
174
+    else
175
+        m_modules.push_back(module);
176
+}

+ 8
- 2
src/framework/core/modulemanager.h View File

@@ -34,12 +34,18 @@ public:
34 34
     ModulePtr discoverModule(const std::string& moduleFile);
35 35
     void ensureModuleLoaded(const std::string& moduleName);
36 36
     void unloadModules();
37
+    void reloadModules();
37 38
 
38 39
     ModulePtr getModule(const std::string& moduleName);
39
-    std::vector<ModulePtr> getModules() { return m_modules; }
40
+    std::deque<ModulePtr> getModules() { return m_modules; }
41
+
42
+protected:
43
+    void updateModuleLoadOrder(ModulePtr module);
44
+
45
+    friend class Module;
40 46
 
41 47
 private:
42
-    std::vector<ModulePtr> m_modules;
48
+    std::deque<ModulePtr> m_modules;
43 49
     std::multimap<int, ModulePtr> m_autoLoadModules;
44 50
 };
45 51
 

+ 10
- 2
src/framework/graphics/fontmanager.cpp View File

@@ -48,8 +48,16 @@ bool FontManager::importFont(std::string fontFile)
48 48
         OTMLNodePtr fontNode = doc->at("Font");
49 49
 
50 50
         std::string name = fontNode->valueAt("name");
51
-        if(fontExists(name))
52
-            Fw::throwException("font '", name, "' already exists, cannot have duplicate font names");
51
+        //if(fontExists(name))
52
+        //    Fw::throwException("font '", name, "' already exists, cannot have duplicate font names");
53
+
54
+        // remove any font with the same name
55
+        for(auto it = m_fonts.begin(); it != m_fonts.end(); ++it) {
56
+            if((*it)->getName() == name) {
57
+                m_fonts.erase(it);
58
+                break;
59
+            }
60
+        }
53 61
 
54 62
         FontPtr font(new Font(name));
55 63
         font->load(fontNode);

+ 9
- 0
src/framework/luafunctions.cpp View File

@@ -47,11 +47,15 @@ void Application::registerLuaFunctions()
47 47
 
48 48
     // Event
49 49
     g_lua.registerClass<Event>();
50
+    g_lua.bindClassMemberFunction<Event>("cancel", &Event::cancel);
51
+    g_lua.bindClassMemberFunction<Event>("execute", &Event::execute);
50 52
     g_lua.bindClassMemberFunction<Event>("isCanceled", &Event::isCanceled);
51 53
     g_lua.bindClassMemberFunction<Event>("isExecuted", &Event::isExecuted);
52 54
 
53 55
     // ScheduledEvent
54 56
     g_lua.registerClass<ScheduledEvent, Event>();
57
+    g_lua.bindClassMemberFunction<ScheduledEvent>("reamaningTicks", &ScheduledEvent::reamaningTicks);
58
+    g_lua.bindClassMemberFunction<ScheduledEvent>("ticks", &ScheduledEvent::ticks);
55 59
 
56 60
     // UIWidget
57 61
     g_lua.registerClass<UIWidget>();
@@ -370,6 +374,8 @@ void Application::registerLuaFunctions()
370 374
     g_lua.registerClass<Module>();
371 375
     g_lua.bindClassMemberFunction<Module>("load", &Module::load);
372 376
     g_lua.bindClassMemberFunction<Module>("unload", &Module::unload);
377
+    g_lua.bindClassMemberFunction<Module>("reload", &Module::reload);
378
+    g_lua.bindClassMemberFunction<Module>("canUnload", &Module::canUnload);
373 379
     g_lua.bindClassMemberFunction<Module>("isLoaded", &Module::isLoaded);
374 380
     g_lua.bindClassMemberFunction<Module>("getDescription", &Module::getDescription);
375 381
     g_lua.bindClassMemberFunction<Module>("getName", &Module::getName);
@@ -397,6 +403,8 @@ void Application::registerLuaFunctions()
397 403
     // Application
398 404
     g_lua.registerStaticClass("g_app");
399 405
     g_lua.bindClassStaticFunction("g_app", "exit", std::bind(&Application::exit, g_app));
406
+    g_lua.bindClassStaticFunction("g_app", "isRunning", std::bind(&Application::isRunning, g_app));
407
+    g_lua.bindClassStaticFunction("g_app", "isStopping", std::bind(&Application::isStopping, g_app));
400 408
 
401 409
     // ConfigManager
402 410
     g_lua.registerStaticClass("g_configs");
@@ -477,6 +485,7 @@ void Application::registerLuaFunctions()
477 485
     g_lua.bindClassStaticFunction("g_modules", "discoverModule", std::bind(&ModuleManager::discoverModule, &g_modules, _1));
478 486
     g_lua.bindClassStaticFunction("g_modules", "ensureModuleLoaded", std::bind(&ModuleManager::ensureModuleLoaded, &g_modules, _1));
479 487
     g_lua.bindClassStaticFunction("g_modules", "unloadModules", std::bind(&ModuleManager::unloadModules, &g_modules));
488
+    g_lua.bindClassStaticFunction("g_modules", "reloadModules", std::bind(&ModuleManager::reloadModules, &g_modules));
480 489
     g_lua.bindClassStaticFunction("g_modules", "getModule", std::bind(&ModuleManager::getModule, &g_modules, _1));
481 490
     g_lua.bindClassStaticFunction("g_modules", "getModules", std::bind(&ModuleManager::getModules, &g_modules));
482 491
 

+ 19
- 0
src/framework/luascript/luavaluecasts.h View File

@@ -132,6 +132,9 @@ bool luavalue_cast(int index, std::vector<T>& vec);
132 132
 template<class T>
133 133
 void push_luavalue(const std::deque<T>& vec);
134 134
 
135
+template<typename T>
136
+bool luavalue_cast(int index, std::deque<T>& vec);
137
+
135 138
 // tuple
136 139
 template<typename... Args>
137 140
 void push_luavalue(const std::tuple<Args...>& tuple);
@@ -279,6 +282,22 @@ void push_luavalue(const std::deque<T>& vec) {
279 282
     }
280 283
 }
281 284
 
285
+template<typename T>
286
+bool luavalue_cast(int index, std::deque<T>& vec)
287
+{
288
+    if(g_lua.isTable(index)) {
289
+        g_lua.pushNil();
290
+        while(g_lua.next(index < 0 ? index-1 : index)) {
291
+            T value;
292
+            if(luavalue_cast(-1, value))
293
+                vec.push_back(value);
294
+            g_lua.pop();
295
+        }
296
+        return true;
297
+    }
298
+    return false;
299
+}
300
+
282 301
 template<int N>
283 302
 struct push_tuple_luavalue {
284 303
     template<typename Tuple>

+ 14
- 11
src/framework/ui/uiwidget.cpp View File

@@ -554,15 +554,17 @@ void UIWidget::destroy()
554 554
 
555 555
 #ifdef DEBUG
556 556
     auto self = asUIWidget();
557
-    g_lua.collectGarbage();
558
-    g_dispatcher.addEvent([self] {
557
+    if(self != g_ui.getRootWidget()) {
559 558
         g_lua.collectGarbage();
560 559
         g_dispatcher.addEvent([self] {
561 560
             g_lua.collectGarbage();
562
-            if(self->getUseCount() != 1)
563
-                logWarning("widget '", self->getId(), "' destroyed but still have ", self->getUseCount()-1, " reference(s) left");
561
+            g_dispatcher.addEvent([self] {
562
+                g_lua.collectGarbage();
563
+                if(self->getUseCount() != 1)
564
+                    logWarning("widget '", self->getId(), "' destroyed but still have ", self->getUseCount()-1, " reference(s) left");
565
+            });
564 566
         });
565
-    });
567
+    }
566 568
 #endif
567 569
 
568 570
     m_destroyed = true;
@@ -853,9 +855,9 @@ UIWidgetPtr UIWidget::getChildById(const std::string& childId)
853 855
 UIWidgetPtr UIWidget::getChildByPos(const Point& childPos)
854 856
 {
855 857
     for(auto it = m_children.rbegin(); it != m_children.rend(); ++it) {
856
-        const UIWidgetPtr& widget = (*it);
857
-        if(widget->isExplicitlyVisible() && widget->containsPoint(childPos))
858
-            return widget;
858
+        const UIWidgetPtr& child = (*it);
859
+        if(child->isExplicitlyVisible() && child->containsPoint(childPos))
860
+            return child;
859 861
     }
860 862
 
861 863
     return nullptr;
@@ -884,7 +886,8 @@ UIWidgetPtr UIWidget::recursiveGetChildById(const std::string& id)
884 886
 
885 887
 UIWidgetPtr UIWidget::recursiveGetChildByPos(const Point& childPos)
886 888
 {
887
-    for(const UIWidgetPtr& child : m_children) {
889
+    for(auto it = m_children.rbegin(); it != m_children.rend(); ++it) {
890
+        const UIWidgetPtr& child = (*it);
888 891
         if(child->isExplicitlyVisible() && child->containsPoint(childPos)) {
889 892
             if(UIWidgetPtr subChild = child->recursiveGetChildByPos(childPos))
890 893
                 return subChild;
@@ -1280,9 +1283,9 @@ bool UIWidget::propagateOnKeyPress(uchar keyCode, int keyboardModifiers, int aut
1280 1283
             return true;
1281 1284
     }
1282 1285
 
1283
-    if(autoRepeatTicks == 0 || autoRepeatTicks >= m_autoRepeatDelay) {
1286
+    if(autoRepeatTicks == 0 || autoRepeatTicks >= m_autoRepeatDelay)
1284 1287
         return onKeyPress(keyCode, keyboardModifiers, autoRepeatTicks);
1285
-    } else
1288
+    else
1286 1289
         return false;
1287 1290
 }
1288 1291
 

+ 1
- 1
src/framework/ui/uiwidgettext.cpp View File

@@ -86,7 +86,7 @@ void UIWidget::onFontChange(const std::string& font)
86 86
 
87 87
 void UIWidget::wrapText()
88 88
 {
89
-    setText(m_font->wrapText(m_text, getWidth()));
89
+    setText(m_font->wrapText(m_text, getWidth() - m_textOffset.x));
90 90
 }
91 91
 
92 92
 void UIWidget::setText(const std::string& text)

+ 11
- 4
src/otclient/otclient.cpp View File

@@ -37,11 +37,18 @@ void OTClient::init(const std::vector<std::string>& args)
37 37
     Application::init(args, Fw::AppEnableAll);
38 38
 
39 39
     g_modules.discoverModules();
40
-    g_modules.autoLoadModules(100);
41
-    g_modules.ensureModuleLoaded("client");
42
-    g_modules.autoLoadModules(1000);
43 40
 
44
-    //g_map.load();
41
+    // core modules 0-99
42
+    g_modules.autoLoadModules(99);
43
+    g_modules.ensureModuleLoaded("core_lib");
44
+    // client modules 100-499
45
+    g_modules.autoLoadModules(499);
46
+    g_modules.ensureModuleLoaded("client_main");
47
+    // game modules 500-999
48
+    g_modules.autoLoadModules(999);
49
+    g_modules.ensureModuleLoaded("game");
50
+    // addons 1000-9999
51
+    g_modules.autoLoadModules(9999);
45 52
 
46 53
     // load otclientrc.lua
47 54
     if(g_resources.fileExists("/otclientrc.lua")) {

Loading…
Cancel
Save