Browse Source

Multi-protocol

Lots of chagnes to add multi protocol flexibility, not really
completed yet, still have to rework text messages opcodes and other stuff,
so this still a working in progress feature

* Rework dat reader, the dat reader can now
* dinamically detect dat version
* Split game into gamelib and game_interface
* Lots of other minor changes
Eduardo Bart 8 years ago
parent
commit
eb24d6776e
64 changed files with 535 additions and 585 deletions
  1. 5
    3
      init.lua
  2. 0
    1
      modules/client/client.otmod
  3. 0
    1
      modules/client_background/background.lua
  4. 1
    1
      modules/client_entergame/characterlist.lua
  5. 19
    3
      modules/client_entergame/entergame.lua
  6. 25
    8
      modules/client_entergame/entergame.otui
  7. 1
    1
      modules/client_extended/extended.lua
  8. 1
    0
      modules/corelib/util.lua
  9. 0
    44
      modules/game/game.otmod
  10. 2
    2
      modules/game_battle/battle.lua
  11. 24
    0
      modules/game_interface/interface.otmod
  12. 0
    3
      modules/game_market/market.otmod
  13. 1
    1
      modules/game_market/marketoffer.lua
  14. 9
    27
      modules/game_market/marketprotocol.lua
  15. 22
    7
      modules/gamelib/const.lua
  16. 31
    23
      modules/gamelib/creature.lua
  17. 47
    0
      modules/gamelib/game.lua
  18. 20
    0
      modules/gamelib/gamelib.otmod
  19. 0
    0
      modules/gamelib/icons/emblem_blue.png
  20. 0
    0
      modules/gamelib/icons/emblem_green.png
  21. 0
    0
      modules/gamelib/icons/emblem_red.png
  22. 0
    0
      modules/gamelib/icons/shield_blue.png
  23. 0
    0
      modules/gamelib/icons/shield_blue_not_shared.png
  24. 0
    0
      modules/gamelib/icons/shield_blue_shared.png
  25. 0
    0
      modules/gamelib/icons/shield_blue_white.png
  26. 0
    0
      modules/gamelib/icons/shield_yellow.png
  27. 0
    0
      modules/gamelib/icons/shield_yellow_not_shared.png
  28. 0
    0
      modules/gamelib/icons/shield_yellow_shared.png
  29. 0
    0
      modules/gamelib/icons/shield_yellow_white.png
  30. 0
    0
      modules/gamelib/icons/skull_black.png
  31. 0
    0
      modules/gamelib/icons/skull_green.png
  32. 0
    0
      modules/gamelib/icons/skull_orange.png
  33. 0
    0
      modules/gamelib/icons/skull_red.png
  34. 0
    0
      modules/gamelib/icons/skull_white.png
  35. 0
    0
      modules/gamelib/icons/skull_yellow.png
  36. 0
    0
      modules/gamelib/market.lua
  37. 0
    1
      modules/gamelib/player.lua
  38. 23
    0
      modules/gamelib/protocolgame.lua
  39. 19
    6
      modules/gamelib/protocollogin.lua
  40. 11
    0
      src/framework/core/application.cpp
  41. 1
    0
      src/framework/core/application.h
  42. 11
    7
      src/framework/luafunctions.cpp
  43. 1
    1
      src/framework/net/inputmessage.cpp
  44. 3
    1
      src/framework/net/inputmessage.h
  45. 1
    1
      src/framework/net/outputmessage.cpp
  46. 1
    1
      src/framework/net/outputmessage.h
  47. 21
    25
      src/otclient/const.h
  48. 2
    0
      src/otclient/creature.cpp
  49. 2
    0
      src/otclient/effect.cpp
  50. 19
    31
      src/otclient/game.cpp
  51. 3
    4
      src/otclient/game.h
  52. 8
    3
      src/otclient/item.cpp
  53. 1
    1
      src/otclient/luafunctions.cpp
  54. 1
    1
      src/otclient/map.cpp
  55. 2
    0
      src/otclient/missile.cpp
  56. 1
    1
      src/otclient/outfit.cpp
  57. 0
    42
      src/otclient/protocolcodes.h
  58. 21
    24
      src/otclient/protocolgameparse.cpp
  59. 8
    6
      src/otclient/protocolgamesend.cpp
  60. 4
    9
      src/otclient/thing.h
  61. 64
    162
      src/otclient/thingtypedat.cpp
  62. 97
    131
      src/otclient/thingtypedat.h
  63. 1
    1
      src/otclient/tile.cpp
  64. 1
    1
      src/otclient/uiitem.cpp

+ 5
- 3
init.lua View File

@@ -29,17 +29,18 @@ g_configs.load("/config.otml")
29 29
 
30 30
 g_modules.discoverModules()
31 31
 
32
-g_modules.autoLoadModules(99)
32
+-- libraries modules 0-99
33
+g_modules.autoLoadModules(99);
33 34
 g_modules.ensureModuleLoaded("corelib")
35
+g_modules.ensureModuleLoaded("gamelib")
34 36
 
35 37
 -- client modules 100-499
36 38
 g_modules.autoLoadModules(499)
37 39
 g_modules.ensureModuleLoaded("client")
38 40
 
39 41
 -- game modules 500-999
40
-g_modules.autoLoadModules(999)
41
-g_modules.ensureModuleLoaded("game")
42
+g_modules.autoLoadModules(999);
43
+g_modules.ensureModuleLoaded("game_interface")
42 44
 
43 45
 -- mods 1000-9999
44 46
 g_modules.autoLoadModules(9999)

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

@@ -16,7 +16,6 @@ Module
16 16
     - client_terminal
17 17
     - client_modulemanager
18 18
     - client_entergame
19
-    - game
20 19
 
21 20
   @onLoad: |
22 21
     dofile 'client'

+ 0
- 1
modules/client_background/background.lua View File

@@ -11,7 +11,6 @@ function Background.init()
11 11
   local clientVersionLabel = background:getChildById('clientVersionLabel')
12 12
   clientVersionLabel:setText('OTClient ' .. g_app.getVersion() .. '\n' ..
13 13
                              'Rev  ' .. g_app.getBuildRevision() .. ' ('.. g_app.getBuildCommit() .. ')\n' ..
14
-                             'Protocol  ' .. g_game.getProtocolVersion() .. '\n' ..
15 14
                              'Built on ' .. g_app.getBuildDate())
16 15
 
17 16
   if not g_game.isOnline() then

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

@@ -54,7 +54,7 @@ local function updateWait(timeStart, timeEnd)
54 54
       progressBar:setPercent(percent)
55 55
 
56 56
       local label = waitingWindow:getChildById('timeLabel')
57
-      label:setText('Trying to reconnect in ' .. timeStr .. ' seconds.')
57
+      label:setText(tr('Trying to reconnect in %s seconds.', timeStr))
58 58
 
59 59
       updateWaitEvent = scheduleEvent(function() updateWait(timeStart, timeEnd) end, 1000 * progressBar:getPercentPixels() / 100 * (timeEnd - timeStart))
60 60
       return true

+ 19
- 3
modules/client_entergame/entergame.lua View File

@@ -5,6 +5,7 @@ local loadBox
5 5
 local enterGame
6 6
 local motdButton
7 7
 local enterGameButton
8
+local protocolBox
8 9
 
9 10
 -- private functions
10 11
 local function onError(protocol, message, errorCode)
@@ -66,6 +67,11 @@ function EnterGame.init()
66 67
   local host = g_settings.get('host')
67 68
   local port = g_settings.get('port')
68 69
   local autologin = g_settings.getBoolean('autologin')
70
+  local protocol = g_settings.getInteger('protocol', 860)
71
+
72
+  if not protocol or protocol == 0 then
73
+    protocol = 860
74
+  end
69 75
 
70 76
   if port == nil or port == 0 then port = 7171 end
71 77
 
@@ -77,6 +83,12 @@ function EnterGame.init()
77 83
   enterGame:getChildById('rememberPasswordBox'):setChecked(#account > 0)
78 84
   enterGame:getChildById('accountNameTextEdit'):focus()
79 85
 
86
+  protocolBox = enterGame:getChildById('protocolComboBox')
87
+  for _i,proto in pairs(g_game.getSupportedProtocols()) do
88
+    protocolBox:addOption(proto)
89
+  end
90
+  protocolBox:setCurrentOption(protocol)
91
+
80 92
   -- only open entergame when app starts
81 93
   if not g_app.isRunning() then
82 94
     if #host > 0 and #password > 0 and #account > 0 and autologin then
@@ -95,6 +107,7 @@ function EnterGame.terminate()
95 107
   enterGameButton = nil
96 108
   motdButton:destroy()
97 109
   motdButton = nil
110
+  protocolBox = nil
98 111
   EnterGame = nil
99 112
 end
100 113
 
@@ -116,7 +129,6 @@ function EnterGame.openWindow()
116 129
   end
117 130
 end
118 131
 
119
-
120 132
 function EnterGame.clearAccountFields()
121 133
   enterGame:getChildById('accountNameTextEdit'):clearText()
122 134
   enterGame:getChildById('accountPasswordTextEdit'):clearText()
@@ -130,16 +142,18 @@ function EnterGame.doLogin()
130 142
   G.password = enterGame:getChildById('accountPasswordTextEdit'):getText()
131 143
   G.host = enterGame:getChildById('serverHostTextEdit'):getText()
132 144
   G.port = tonumber(enterGame:getChildById('serverPortTextEdit'):getText())
145
+  local protocol = tonumber(protocolBox:getText())
133 146
   EnterGame.hide()
134 147
 
135
-  if G.host == '' or G.port == nil or G.port == 0 then
136
-    local errorBox = displayErrorBox(tr('Login Error'), tr('Enter a valid server host and port to login.'))
148
+  if g_game.isOnline() then
149
+    local errorBox = displayErrorBox(tr('Login Error'), tr('Cannot login while already in game.'))
137 150
     connect(errorBox, { onOk = EnterGame.show })
138 151
     return
139 152
   end
140 153
 
141 154
   g_settings.set('host', G.host)
142 155
   g_settings.set('port', G.port)
156
+  g_settings.set('protocol', protocol)
143 157
 
144 158
   local protocolLogin = ProtocolLogin.create()
145 159
   protocolLogin.onError = onError
@@ -153,6 +167,8 @@ function EnterGame.doLogin()
153 167
                                   EnterGame.show()
154 168
                                 end })
155 169
 
170
+  g_game.chooseRsa(G.host)
171
+  g_game.setProtocolVersion(protocol)
156 172
   protocolLogin:login(G.host, G.port, G.account, G.password)
157 173
 end
158 174
 

+ 25
- 8
modules/client_entergame/entergame.otui View File

@@ -1,7 +1,7 @@
1 1
 MainWindow
2 2
   id: enterGame
3 3
   !text: tr('Enter Game')
4
-  size: 236 240
4
+  size: 236 274
5 5
   @onEnter: EnterGame.doLogin()
6 6
   @onEscape: EnterGame.hide()
7 7
 
@@ -43,26 +43,43 @@ MainWindow
43 43
   TextEdit
44 44
     id: serverHostTextEdit
45 45
     !tooltip: tr('Make sure that your client uses\nthe correct game protocol version')
46
-    anchors.left: serverLabel.left
46
+    anchors.left: parent.left
47
+    anchors.right: parent.right
47 48
     anchors.top: serverLabel.bottom
48 49
     margin-top: 2
49
-    width: 140
50
+
51
+  Label
52
+    id: protocolLabel
53
+    !text: tr('Protocol')
54
+    anchors.left: parent.left
55
+    anchors.top: serverHostTextEdit.bottom
56
+    anchors.right: portLabel.left
57
+    margin-right: 10
58
+    margin-top: 8
59
+
60
+  ComboBox
61
+    id: protocolComboBox
62
+    anchors.left: protocolLabel.left
63
+    anchors.right: protocolLabel.right
64
+    anchors.top: protocolLabel.bottom
65
+    margin-top: 2
66
+    width: 90
50 67
 
51 68
   Label
52 69
     id: portLabel
53 70
     !text: tr('Port')
54
-    anchors.left: serverHostTextEdit.right
55
-    anchors.top: serverLabel.top
56
-    margin-left: 10
57
-    text-auto-resize: true
71
+    anchors.right: parent.right
72
+    anchors.top: serverHostTextEdit.bottom
73
+    margin-top: 8
74
+    width: 70
58 75
 
59 76
   TextEdit
60 77
     id: serverPortTextEdit
61 78
     text: 7171
79
+    anchors.right: parent.right
62 80
     anchors.left: portLabel.left
63 81
     anchors.top: portLabel.bottom
64 82
     margin-top: 2
65
-    width: 55
66 83
 
67 84
   CheckBox
68 85
     id: rememberPasswordBox

+ 1
- 1
modules/client_extended/extended.lua View File

@@ -55,4 +55,4 @@ function Extended.unregister(opcode)
55 55
 
56 56
   callbacks[opcode] = nil
57 57
   return true
58
-end
58
+end

+ 1
- 0
modules/corelib/util.lua View File

@@ -132,6 +132,7 @@ function getfsrcpath(depth)
132 132
 end
133 133
 
134 134
 function resolvepath(filePath, depth)
135
+  if not filePath then return nil end
135 136
   depth = depth or 1
136 137
   if filePath then
137 138
     if filePath:sub(0, 1) ~= '/' then

+ 0
- 44
modules/game/game.otmod View File

@@ -1,44 +0,0 @@
1
-Module
2
-  name: game
3
-  description: Contains game related classes
4
-  author: OTClient team
5
-  website: www.otclient.info
6
-
7
-  dependencies:
8
-    - client_extended
9
-    - client_background
10
-    - game_tibiafiles
11
-
12
-  load-later:
13
-    - game_interface
14
-    - game_hotkeys
15
-    - game_questlog
16
-    - game_textmessage
17
-    - game_console
18
-    - game_outfit
19
-    - game_healthinfo
20
-    - game_skills
21
-    - game_inventory
22
-    - game_combatcontrols
23
-    - game_containers
24
-    - game_viplist
25
-    - game_battle
26
-    - game_minimap
27
-    - game_npctrade
28
-    - game_textwindow
29
-    - game_playertrade
30
-    - game_ruleviolation
31
-    - game_bugreport
32
-    - game_shaders
33
-    - game_playerdeath
34
-    - game_playermount
35
-    - game_market
36
-
37
-  @onLoad: |
38
-    dofile 'const'
39
-
40
-    dofile 'protocollogin'
41
-
42
-    dofile 'creature'
43
-    dofile 'player'
44
-    dofile 'market'

+ 2
- 2
modules/game_battle/battle.lua View File

@@ -224,7 +224,7 @@ function Battle.checkCreatureSkull(creature, skullId)
224 224
       if creature:getSkull() ~= SkullNone then
225 225
         skullWidget:setWidth(skullWidget:getHeight())
226 226
         local imagePath = getSkullImagePath(creature:getSkull())
227
-        skullWidget:setImageSource('/game/' .. imagePath)
227
+        skullWidget:setImageSource(imagePath)
228 228
         labelWidget:setMarginLeft(5)
229 229
       else
230 230
         skullWidget:setWidth(0)
@@ -246,7 +246,7 @@ function Battle.checkCreatureEmblem(creature, emblemId)
246 246
       if emblemId ~= EmblemNone then
247 247
         emblemWidget:setWidth(emblemWidget:getHeight())
248 248
         local imagePath = getEmblemImagePath(emblemId)
249
-        emblemWidget:setImageSource('/game/' .. imagePath)
249
+        emblemWidget:setImageSource(imagePath)
250 250
         emblemWidget:setMarginLeft(5)
251 251
         labelWidget:setMarginLeft(5)
252 252
       else

+ 24
- 0
modules/game_interface/interface.otmod View File

@@ -4,6 +4,30 @@ Module
4 4
   author: OTClient team
5 5
   website: www.otclient.info
6 6
 
7
+  load-later:
8
+    - game_hotkeys
9
+    - game_questlog
10
+    - game_textmessage
11
+    - game_console
12
+    - game_outfit
13
+    - game_healthinfo
14
+    - game_skills
15
+    - game_inventory
16
+    - game_combatcontrols
17
+    - game_containers
18
+    - game_viplist
19
+    - game_battle
20
+    - game_minimap
21
+    - game_npctrade
22
+    - game_textwindow
23
+    - game_playertrade
24
+    - game_ruleviolation
25
+    - game_bugreport
26
+    - game_shaders
27
+    - game_playerdeath
28
+    - game_playermount
29
+    - game_market
30
+
7 31
   @onLoad: |
8 32
     dofile 'widgets/uigamemap'
9 33
     dofile 'widgets/uiitem'

+ 0
- 3
modules/game_market/market.otmod View File

@@ -4,9 +4,6 @@ Module
4 4
   author: BeniS
5 5
   website: www.otclient.info
6 6
 
7
-  dependencies:
8
-    - game
9
-
10 7
   @onLoad: |
11 8
     dofile 'marketoffer'
12 9
     dofile 'marketprotocol'

+ 1
- 1
modules/game_market/marketoffer.lua View File

@@ -32,7 +32,7 @@ MarketOffer.new = function(offerId, action, itemId, amount, price, playerName, s
32 32
   offer.player = playerName
33 33
 
34 34
   state = tonumber(state)
35
-  if state ~= MarketOfferState.Active and state ~= MarketOfferState.Cancelled 
35
+  if state ~= MarketOfferState.Active and state ~= MarketOfferState.Cancelled
36 36
     and state ~= MarketOfferState.Expired and state ~= MarketOfferState.Accepted then
37 37
     g_logger.error('MarketOffer.new - invalid state provided.')
38 38
   end

+ 9
- 27
modules/game_market/marketprotocol.lua View File

@@ -3,27 +3,6 @@ MarketProtocol = {}
3 3
 local market
4 4
 
5 5
 -- private functions
6
-
7
-local function parseOpcode(protocol, opcode, msg)
8
-  if not g_game.getFeature(GamePlayerMarket) then
9
-    return false
10
-  end
11
-
12
-  -- process msg
13
-  if opcode == GameServerOpcodes.GameServerMarketEnter then
14
-    parseMarketEnter(msg)
15
-  elseif opcode == GameServerOpcodes.GameServerMarketLeave then
16
-    parseMarketLeave(msg)
17
-  elseif opcode == GameServerOpcodes.GameServerMarketDetail then
18
-    parseMarketDetail(msg)
19
-  elseif opcode == GameServerOpcodes.GameServerMarketBrowse then
20
-    parseMarketBrowse(msg)
21
-  else
22
-    return false
23
-  end
24
-  return true
25
-end
26
-
27 6
 local function send(msg)
28 7
   print(msg:getMessageSize())
29 8
   g_game.getProtocolGame():safeSend(msg)
@@ -49,12 +28,11 @@ local function readMarketOffer(msg, action, var)
49 28
   else
50 29
     playerName = msg:getString()
51 30
   end
52
-  
31
+
53 32
   return MarketOffer.new({timestamp, counter}, action, itemId, amount, price, playerName, state)
54 33
 end
55 34
 
56 35
 -- parsing protocols
57
-
58 36
 local function parseMarketEnter(msg)
59 37
   local balance = msg:getU32()
60 38
   local offers = msg:getU8()
@@ -128,14 +106,18 @@ local function parseMarketBrowse(msg)
128 106
 end
129 107
 
130 108
 -- public functions
131
-
132 109
 function MarketProtocol.init()
133
-  connect(ProtocolGame, { onOpcode = parseOpcode } )
134
-
110
+  ProtocolGame.registerOpcode(GameServerOpcodes.GameServerMarketEnter, parseMarketEnter)
111
+  ProtocolGame.registerOpcode(GameServerOpcodes.GameServerMarketLeave, parseMarketLeave)
112
+  ProtocolGame.registerOpcode(GameServerOpcodes.GameServerMarketDetail, parseMarketDetail)
113
+  ProtocolGame.registerOpcode(GameServerOpcodes.GameServerMarketBrowse, parseMarketBrowse)
135 114
 end
136 115
 
137 116
 function MarketProtocol.terminate()
138
-  disconnect(ProtocolGame, { onOpcode = parseOpcode } )
117
+  ProtocolGame.unregisterOpcode(GameServerOpcodes.GameServerMarketEnter, parseMarketEnter)
118
+  ProtocolGame.unregisterOpcode(GameServerOpcodes.GameServerMarketLeave, parseMarketLeave)
119
+  ProtocolGame.unregisterOpcode(GameServerOpcodes.GameServerMarketDetail, parseMarketDetail)
120
+  ProtocolGame.unregisterOpcode(GameServerOpcodes.GameServerMarketBrowse, parseMarketBrowse)
139 121
 
140 122
   market = nil
141 123
   MarketProtocol = nil

modules/game/const.lua → modules/gamelib/const.lua View File

@@ -34,11 +34,6 @@ SouthEast = 5
34 34
 SouthWest = 6
35 35
 NorthWest = 7
36 36
 
37
-LoginServerError = 10
38
-LoginServerMotd = 20
39
-LoginServerUpdateNeeded = 30
40
-LoginServerCharacterList = 100
41
-
42 37
 GameExtendedOpcode = 0
43 38
 GameProtocolChecksum = 1
44 39
 GameAccountNames = 2
@@ -56,7 +51,7 @@ GameChannelPlayerList = 13
56 51
 GamePlayerMounts = 14
57 52
 GameEnvironmentEffect = 15
58 53
 GameCreatureType = 16
59
-GameCreatureAdditionalInfo = 17
54
+GameCreatureEmblems = 17
60 55
 GameCreaturePassableInfo = 18
61 56
 GameItemAnimationPhase = 19
62 57
 GameTrucatedPingOpcode = 20
@@ -64,6 +59,25 @@ GameReverseCreatureStack = 21
64 59
 GameMagicEffectU16 = 22
65 60
 GamePlayerMarket = 23
66 61
 
67
-OTSERV_RSA = "109120132967399429278860960508995541528237502902798129123468757937266291492576446330739696001110603907230888610072655818825358503429057592827629436413108566029093628212635953836686562675849720620786279431090218017681061521755056710823876476444260558147179707119674283982419152118103759076030616683978566631413"
62
+OTSERV_RSA  = "1091201329673994292788609605089955415282375029027981291234687579" ..
63
+              "3726629149257644633073969600111060390723088861007265581882535850" ..
64
+              "3429057592827629436413108566029093628212635953836686562675849720" ..
65
+              "6207862794310902180176810615217550567108238764764442605581471797" ..
66
+              "07119674283982419152118103759076030616683978566631413"
67
+
68
+CIPSOFT_RSA = "1321277432058722840622950990822933849527763264961655079678763618" ..
69
+              "4334395343554449668205332383339435179772895415509701210392836078" ..
70
+              "6959821132214473291575712138800495033169914814069637740318278150" ..
71
+              "2907336840325241747827401343576296990629870233111328210165697754" ..
72
+              "88792221429527047321331896351555606801473202394175817"
73
+
74
+OsTypes = {
75
+  Linux = 1,
76
+  Windows = 2,
77
+  Flash = 3,
78
+  OtclientLinux = 10,
79
+  OtclientWindows = 11,
80
+  OtclientMac = 12
81
+}
68 82
 
83
+-- @}

modules/game/creature.lua → modules/gamelib/creature.lua View File

@@ -30,73 +30,81 @@ EmblemBlue = 3
30 30
 -- @}
31 31
 
32 32
 function getSkullImagePath(skullId)
33
+  local path
33 34
   if skullId == SkullYellow then
34
-    return 'icons/skull_yellow.png'
35
+    path = 'icons/skull_yellow.png'
35 36
   elseif skullId == SkullGreen then
36
-    return 'icons/skull_green.png'
37
+    path = 'icons/skull_green.png'
37 38
   elseif skullId == SkullWhite then
38
-    return 'icons/skull_white.png'
39
+    path = 'icons/skull_white.png'
39 40
   elseif skullId == SkullRed then
40
-    return 'icons/skull_red.png'
41
+    path = 'icons/skull_red.png'
41 42
   elseif skullId == SkullBlack then
42
-    return 'icons/skull_black.png'
43
+    path = 'icons/skull_black.png'
43 44
   elseif skullId == SkullOrange then
44
-    return 'icons/skull_orange.png'
45
+    path = 'icons/skull_orange.png'
45 46
   end
47
+  path = resolvepath(path)
48
+  return path
46 49
 end
47 50
 
48 51
 function getShieldImagePathAndBlink(shieldId)
52
+  local path
49 53
   if shieldId == ShieldWhiteYellow then
50
-    return 'icons/shield_yellow_white.png', false
54
+    path = 'icons/shield_yellow_white.png', false
51 55
   elseif shieldId == ShieldWhiteBlue then
52
-    return 'icons/shield_blue_white.png', false
56
+    path = 'icons/shield_blue_white.png', false
53 57
   elseif shieldId == ShieldBlue then
54
-    return 'icons/shield_blue.png', false
58
+    path = 'icons/shield_blue.png', false
55 59
   elseif shieldId == ShieldYellow then
56
-    return 'icons/shield_yellow.png', false
60
+    path = 'icons/shield_yellow.png', false
57 61
   elseif shieldId == ShieldBlueSharedExp then
58
-    return 'icons/shield_blue_shared.png', false
62
+    path = 'icons/shield_blue_shared.png', false
59 63
   elseif shieldId == ShieldYellowSharedExp then
60
-    return 'icons/shield_yellow_shared.png', false
64
+    path = 'icons/shield_yellow_shared.png', false
61 65
   elseif shieldId == ShieldBlueNoSharedExpBlink then
62
-    return 'icons/shield_blue_not_shared.png', true
66
+    path = 'icons/shield_blue_not_shared.png', true
63 67
   elseif shieldId == ShieldYellowNoSharedExpBlink then
64
-    return 'icons/shield_yellow_not_shared.png', true
68
+    path = 'icons/shield_yellow_not_shared.png', true
65 69
   elseif shieldId == ShieldBlueNoSharedExp then
66
-    return 'icons/shield_blue_not_shared.png', false
70
+    path = 'icons/shield_blue_not_shared.png', false
67 71
   elseif shieldId == ShieldYellowNoSharedExp then
68
-    return 'icons/shield_yellow_not_shared.png', false
72
+    path = 'icons/shield_yellow_not_shared.png', false
69 73
   end
74
+  path = resolvepath(path)
75
+  return path
70 76
 end
71 77
 
72 78
 function getEmblemImagePath(emblemId)
79
+  local path
73 80
   if emblemId == EmblemGreen then
74
-    return 'icons/emblem_green.png'
81
+    path = 'icons/emblem_green.png'
75 82
   elseif emblemId == EmblemRed then
76
-    return 'icons/emblem_red.png'
83
+    path = 'icons/emblem_red.png'
77 84
   elseif emblemId == EmblemBlue then
78
-    return 'icons/emblem_blue.png'
85
+    path = 'icons/emblem_blue.png'
79 86
   end
87
+  path = resolvepath(path)
88
+  return path
80 89
 end
81 90
 
82 91
 function Creature:onSkullChange(skullId)
83 92
   local imagePath = getSkullImagePath(skullId)
84 93
   if imagePath then
85
-    self:setSkullTexture(resolvepath(imagePath))
94
+    self:setSkullTexture(imagePath)
86 95
   end
87 96
 end
88 97
 
89 98
 function Creature:onShieldChange(shieldId)
90 99
   local imagePath, blink = getShieldImagePathAndBlink(shieldId)
91 100
   if imagePath then
92
-    self:setShieldTexture(resolvepath(imagePath), blink)
101
+    self:setShieldTexture(imagePath, blink)
93 102
   end
94 103
 end
95 104
 
96 105
 function Creature:onEmblemChange(emblemId)
97 106
   local imagePath = getEmblemImagePath(emblemId)
98 107
   if imagePath then
99
-    self:setEmblemTexture(resolvepath(imagePath))
108
+    self:setEmblemTexture(imagePath)
100 109
   end
101 110
 end
102
-

+ 47
- 0
modules/gamelib/game.lua View File

@@ -0,0 +1,47 @@
1
+local currentRsa = OTSERV_RSA
2
+
3
+function g_game.getRsa()
4
+  return currentRsa
5
+end
6
+
7
+function g_game.chooseRsa(host)
8
+  if host:match('.*\.tibia\.com') or host:match('.*\.cipsoft\.com') then
9
+    currentRsa = CIPSOFT_RSA
10
+  else
11
+    currentRsa = OTSERV_RSA
12
+  end
13
+end
14
+
15
+function g_game.setRsa(rsa)
16
+  currentRsa = rsa
17
+end
18
+
19
+function g_game.isOfficialTibia()
20
+  return currentRsa == CIPSOFT_RSA
21
+end
22
+
23
+function g_game.getOsType()
24
+  if g_game.isOfficialTibia() then
25
+    if g_app.getOs() == 'windows' then
26
+      return OsTypes.Windows
27
+    else
28
+      return OsTypes.Linux
29
+    end
30
+  else
31
+    if g_app.getOs() == 'windows' then
32
+      return OsTypes.OtclientWindows
33
+    elseif g_app.getOs() == 'mac' then
34
+      return OsTypes.OtclientMac
35
+    else
36
+      return OsTypes.OtclientLinux
37
+    end
38
+  end
39
+end
40
+
41
+function g_game.getSupportedProtocols()
42
+  return {
43
+    810, 853, 854, 860, 861, 862, 870, 940,
44
+    953, 954, 960
45
+  }
46
+end
47
+

+ 20
- 0
modules/gamelib/gamelib.otmod View File

@@ -0,0 +1,20 @@
1
+Module
2
+  name: gamelib
3
+  description: Contains game related classes
4
+  author: OTClient team
5
+  website: www.otclient.info
6
+
7
+  dependencies:
8
+    - client_extended
9
+    - game_tibiafiles
10
+
11
+  @onLoad: |
12
+    dofile 'const'
13
+
14
+    dofile 'protocollogin'
15
+    dofile 'protocolgame'
16
+    dofile 'game'
17
+
18
+    dofile 'creature'
19
+    dofile 'player'
20
+    dofile 'market'

modules/game/icons/emblem_blue.png → modules/gamelib/icons/emblem_blue.png View File


modules/game/icons/emblem_green.png → modules/gamelib/icons/emblem_green.png View File


modules/game/icons/emblem_red.png → modules/gamelib/icons/emblem_red.png View File


modules/game/icons/shield_blue.png → modules/gamelib/icons/shield_blue.png View File


modules/game/icons/shield_blue_not_shared.png → modules/gamelib/icons/shield_blue_not_shared.png View File


modules/game/icons/shield_blue_shared.png → modules/gamelib/icons/shield_blue_shared.png View File


modules/game/icons/shield_blue_white.png → modules/gamelib/icons/shield_blue_white.png View File


modules/game/icons/shield_yellow.png → modules/gamelib/icons/shield_yellow.png View File


modules/game/icons/shield_yellow_not_shared.png → modules/gamelib/icons/shield_yellow_not_shared.png View File


modules/game/icons/shield_yellow_shared.png → modules/gamelib/icons/shield_yellow_shared.png View File


modules/game/icons/shield_yellow_white.png → modules/gamelib/icons/shield_yellow_white.png View File


modules/game/icons/skull_black.png → modules/gamelib/icons/skull_black.png View File


modules/game/icons/skull_green.png → modules/gamelib/icons/skull_green.png View File


modules/game/icons/skull_orange.png → modules/gamelib/icons/skull_orange.png View File


modules/game/icons/skull_red.png → modules/gamelib/icons/skull_red.png View File


modules/game/icons/skull_white.png → modules/gamelib/icons/skull_white.png View File


modules/game/icons/skull_yellow.png → modules/gamelib/icons/skull_yellow.png View File


modules/game/market.lua → modules/gamelib/market.lua View File


modules/game/player.lua → modules/gamelib/player.lua View File

@@ -52,4 +52,3 @@ function Player:hasVip(creatureName)
52 52
   end
53 53
   return false
54 54
 end
55
-

+ 23
- 0
modules/gamelib/protocolgame.lua View File

@@ -0,0 +1,23 @@
1
+local opcodeCallbacks = {}
2
+
3
+function ProtocolGame:onOpcode(opcode, msg)
4
+  for i, callback in pairs(opcodeCallbacks) do
5
+    if i == opcode then
6
+      callback(msg)
7
+      return true
8
+    end
9
+  end
10
+  return false
11
+end
12
+
13
+function ProtocolGame.registerOpcode(opcode, callback)
14
+  if opcodeCallbacks[opcode] then
15
+    error('opcode ' .. opcode .. ' already registered will be overriden')
16
+  end
17
+
18
+  opcodeCallbacks[opcode] = callback
19
+end
20
+
21
+function ProtocolGame.unregisterOpcode(opcode)
22
+  opcodeCallbacks[opcode] = nil
23
+end

modules/game/protocollogin.lua → modules/gamelib/protocollogin.lua View File

@@ -1,16 +1,25 @@
1 1
 -- @docclass
2 2
 ProtocolLogin = extends(Protocol)
3 3
 
4
+-- set to the latest Tibia.pic signature to make otclient compatible with official tibia
5
+local PIC_SIGNATURE = 0
6
+
7
+LoginServerError = 10
8
+LoginServerMotd = 20
9
+LoginServerUpdateNeeded = 30
10
+LoginServerCharacterList = 100
11
+
12
+
4 13
 -- private functions
5 14
 local function sendLoginPacket(protocol)
6 15
   local msg = OutputMessage.create()
7 16
   msg:addU8(ClientOpcodes.ClientEnterAccount)
8
-  msg:addU16(1) -- todo: ClientOs
9
-  msg:addU16(g_game.getClientVersion())
17
+  msg:addU16(g_game.getOsType())
18
+  msg:addU16(g_game.getProtocolVersion())
10 19
 
11 20
   msg:addU32(g_things.getDatSignature())
12 21
   msg:addU32(g_sprites.getSprSignature())
13
-  msg:addU32(0) -- todo: pic signature
22
+  msg:addU32(PIC_SIGNATURE)
14 23
 
15 24
   local paddingBytes = 128
16 25
   msg:addU8(0) -- first RSA byte must be 0
@@ -40,7 +49,7 @@ local function sendLoginPacket(protocol)
40 49
   end
41 50
 
42 51
   msg:addPaddingBytes(paddingBytes, 0)
43
-  msg:encryptRSA(128, OTSERV_RSA) -- todo: check whether to use cip or ot rsa
52
+  msg:encryptRsa(128, g_game.getRsa())
44 53
 
45 54
   protocol:send(msg)
46 55
   protocol:enableXteaEncryption()
@@ -60,7 +69,7 @@ function ProtocolLogin:onRecv(msg)
60 69
     elseif opcode == LoginServerMotd then
61 70
       self:parseMotd(msg)
62 71
     elseif opcode == LoginServerUpdateNeeded then
63
-      signalcall(self.onError, self, "Client needs update.")
72
+      signalcall(self.onError, self, tr("Client needs update."))
64 73
     elseif opcode == LoginServerCharacterList then
65 74
       self:parseCharacterList(msg)
66 75
     else
@@ -77,7 +86,11 @@ end
77 86
 
78 87
 function ProtocolLogin:login(host, port, accountName, accountPassword)
79 88
   if string.len(accountName) == 0 or string.len(accountPassword) == 0 then
80
-    signalcall(self.onError, self, "You must enter an account name and password.")
89
+    signalcall(self.onError, self, tr("You must enter an account name and password."))
90
+    return
91
+  end
92
+  if string.len(host) == 0 or port == nil or port == 0 then
93
+    signalcall(self.onError, self, tr("You must enter a valid server address and port."))
81 94
     return
82 95
   end
83 96
 

+ 11
- 0
src/framework/core/application.cpp View File

@@ -141,3 +141,14 @@ void Application::close()
141 141
     if(!g_lua.callGlobalField<bool>("g_app", "onClose"))
142 142
         exit();
143 143
 }
144
+
145
+std::string Application::getOs()
146
+{
147
+#if defined(WIN32)
148
+    return "windows";
149
+#elif defined(__APPLE__)
150
+    return "mac";
151
+#else
152
+    return "linux";
153
+#endif
154
+}

+ 1
- 0
src/framework/core/application.h View File

@@ -58,6 +58,7 @@ public:
58 58
     std::string getBuildCommit() { return BUILD_COMMIT; }
59 59
     std::string getBuildType() { return BUILD_TYPE; }
60 60
     std::string getBuildArch() { return BUILD_ARCH; }
61
+    std::string getOs();
61 62
     std::string getStartupOptions() { return m_startupOptions; }
62 63
 
63 64
 protected:

+ 11
- 7
src/framework/luafunctions.cpp View File

@@ -22,22 +22,25 @@
22 22
 
23 23
 #include <framework/core/application.h>
24 24
 #include <framework/luaengine/luainterface.h>
25
-#include <framework/graphics/fontmanager.h>
26
-#include <framework/ui/ui.h>
27 25
 #include <framework/net/protocol.h>
28 26
 #include <framework/core/eventdispatcher.h>
29 27
 #include <framework/core/configmanager.h>
30 28
 #include <framework/otml/otml.h>
31
-#include <framework/graphics/graphics.h>
32
-#include <framework/platform/platformwindow.h>
33 29
 #include <framework/core/modulemanager.h>
34 30
 #include <framework/core/module.h>
35 31
 #include <framework/sound/soundmanager.h>
36 32
 #include <framework/util/crypt.h>
37 33
 #include <framework/core/resourcemanager.h>
38
-#include <framework/graphics/particlemanager.h>
39 34
 #include <framework/graphics/texturemanager.h>
40 35
 
36
+#ifdef FW_GRAPHICS
37
+#include <framework/graphics/graphics.h>
38
+#include <framework/platform/platformwindow.h>
39
+#include <framework/graphics/particlemanager.h>
40
+#include <framework/graphics/fontmanager.h>
41
+#include <framework/ui/ui.h>
42
+#endif
43
+
41 44
 void Application::registerLuaFunctions()
42 45
 {
43 46
     // conversion globals
@@ -67,6 +70,7 @@ void Application::registerLuaFunctions()
67 70
     g_lua.bindSingletonFunction("g_app", "getBuildCommit", &Application::getBuildCommit, static_cast<Application*>(&g_app));
68 71
     g_lua.bindSingletonFunction("g_app", "getBuildType", &Application::getBuildType, static_cast<Application*>(&g_app));
69 72
     g_lua.bindSingletonFunction("g_app", "getBuildArch", &Application::getBuildArch, static_cast<Application*>(&g_app));
73
+    g_lua.bindSingletonFunction("g_app", "getOs", &Application::getOs, static_cast<Application*>(&g_app));
70 74
     g_lua.bindSingletonFunction("g_app", "exit", &Application::exit, static_cast<Application*>(&g_app));
71 75
 
72 76
     // Crypt
@@ -644,7 +648,7 @@ void Application::registerLuaFunctions()
644 648
     g_lua.bindClassMemberFunction<InputMessage>("peekU16", &InputMessage::peekU16);
645 649
     g_lua.bindClassMemberFunction<InputMessage>("peekU32", &InputMessage::peekU32);
646 650
     g_lua.bindClassMemberFunction<InputMessage>("peekU64", &InputMessage::peekU64);
647
-    g_lua.bindClassMemberFunction<InputMessage>("decryptRSA", &InputMessage::decryptRSA);
651
+    g_lua.bindClassMemberFunction<InputMessage>("decryptRsa", &InputMessage::decryptRsa);
648 652
     g_lua.bindClassMemberFunction<InputMessage>("getReadSize", &InputMessage::getReadSize);
649 653
     g_lua.bindClassMemberFunction<InputMessage>("getUnreadSize", &InputMessage::getUnreadSize);
650 654
     g_lua.bindClassMemberFunction<InputMessage>("getMessageSize", &InputMessage::getMessageSize);
@@ -661,7 +665,7 @@ void Application::registerLuaFunctions()
661 665
     g_lua.bindClassMemberFunction<OutputMessage>("addU64", &OutputMessage::addU64);
662 666
     g_lua.bindClassMemberFunction<OutputMessage>("addString", &OutputMessage::addString);
663 667
     g_lua.bindClassMemberFunction<OutputMessage>("addPaddingBytes", &OutputMessage::addPaddingBytes);
664
-    g_lua.bindClassMemberFunction<OutputMessage>("encryptRSA", &OutputMessage::encryptRSA);
668
+    g_lua.bindClassMemberFunction<OutputMessage>("encryptRsa", &OutputMessage::encryptRsa);
665 669
     g_lua.bindClassMemberFunction<OutputMessage>("getMessageSize", &OutputMessage::getMessageSize);
666 670
 #endif
667 671
 

+ 1
- 1
src/framework/net/inputmessage.cpp View File

@@ -86,7 +86,7 @@ std::string InputMessage::getString()
86 86
     return std::string(v, stringLength);
87 87
 }
88 88
 
89
-void InputMessage::decryptRSA(int size, const std::string& p, const std::string& q, const std::string& d)
89
+void InputMessage::decryptRsa(int size, const std::string& p, const std::string& q, const std::string& d)
90 90
 {
91 91
     checkRead(size);
92 92
     RSA::decrypt((char*)m_buffer + m_readPos, size, p.c_str(), q.c_str(), d.c_str());

+ 3
- 1
src/framework/net/inputmessage.h View File

@@ -40,6 +40,7 @@ public:
40 40
     void setBuffer(const std::string& buffer);
41 41
 
42 42
     void skipBytes(uint16 bytes) { m_readPos += bytes; }
43
+    void setReadPos(uint16 readPos) { m_readPos = readPos; }
43 44
     uint8 getU8();
44 45
     uint16 getU16();
45 46
     uint32 getU32();
@@ -51,9 +52,10 @@ public:
51 52
     uint32 peekU32() { uint32 v = getU32(); m_readPos-=4; return v; }
52 53
     uint64 peekU64() { uint64 v = getU64(); m_readPos-=8; return v; }
53 54
 
54
-    void decryptRSA(int size, const std::string& p, const std::string& q, const std::string& d);
55
+    void decryptRsa(int size, const std::string& p, const std::string& q, const std::string& d);
55 56
 
56 57
     int getReadSize() { return m_readPos - m_headerPos; }
58
+    int getReadPos() { return m_readPos; }
57 59
     int getUnreadSize() { return m_messageSize - (m_readPos - m_headerPos); }
58 60
     uint16 getMessageSize() { return m_messageSize; }
59 61
 

+ 1
- 1
src/framework/net/outputmessage.cpp View File

@@ -89,7 +89,7 @@ void OutputMessage::addPaddingBytes(int bytes, uint8 byte)
89 89
     m_messageSize += bytes;
90 90
 }
91 91
 
92
-void OutputMessage::encryptRSA(int size, const std::string& key)
92
+void OutputMessage::encryptRsa(int size, const std::string& key)
93 93
 {
94 94
     if(m_messageSize < size)
95 95
         throw stdext::exception("insufficient bytes in buffer to encrypt");

+ 1
- 1
src/framework/net/outputmessage.h View File

@@ -49,7 +49,7 @@ public:
49 49
     void addString(const std::string& buffer);
50 50
     void addPaddingBytes(int bytes, uint8 byte = 0);
51 51
 
52
-    void encryptRSA(int size, const std::string& key);
52
+    void encryptRsa(int size, const std::string& key);
53 53
 
54 54
     uint16 getMessageSize() { return m_messageSize; }
55 55
 

+ 21
- 25
src/otclient/const.h View File

@@ -286,31 +286,27 @@ namespace Otc
286 286
     };
287 287
 
288 288
     enum GameFeature {
289
-        GameExtendedOpcode = 0,
290
-        GameProtocolChecksum,
291
-        GameAccountNames,
292
-        GameChallangeOnLogin,
293
-        GameStackposOnTileAddThing,
294
-        GamePenalityOnDeath,
295
-        GameNameOnNpcTrade,
296
-        GameDoubleFreeCapacity,
297
-        GameDoubleExperience,
298
-        GameTotalCapacity,
299
-        GameSkillsBase,
300
-        GameAdditionalPlayerStats,
301
-        GameIdOnCancelAttack,
302
-        GameChannelPlayerList,
303
-        GamePlayerMounts,
304
-        GameEnvironmentEffect,
305
-        GameCreatureType,
306
-        GameCreatureAdditionalInfo,
307
-        GameCreaturePassableInfo,
308
-        GameItemAnimationPhase,
309
-        GameTrucatedPingOpcode,
310
-        GameReverseCreatureStack,
311
-        GameMagicEffectU16,
312
-        GamePlayerMarket,
313
-        LastGameFeature
289
+        // 1-50 defined in c++
290
+        GameProtocolChecksum = 1,
291
+        GameAccountNames = 2,
292
+        GameChallangeOnLogin = 3,
293
+        GamePenalityOnDeath = 5,
294
+        GameNameOnNpcTrade = 6,
295
+        GameDoubleFreeCapacity = 7,
296
+        GameDoubleExperience = 8,
297
+        GameTotalCapacity = 9,
298
+        GameSkillsBase = 10,
299
+        GamePlayerRegenerationTime = 11,
300
+        GameChannelPlayerList = 13,
301
+        GamePlayerMounts = 14,
302
+        GameEnvironmentEffect = 15,
303
+        GameCreatureEmblems = 17,
304
+        GameItemAnimationPhase = 19,
305
+        GameMagicEffectU16 = 22,
306
+        GamePlayerMarket = 23,
307
+        // 23-50 unused yet
308
+        // 51-100 reserved to be defined in lua
309
+        LastGameFeature = 101
314 310
     };
315 311
 
316 312
     enum PathFindResult {

+ 2
- 0
src/otclient/creature.cpp View File

@@ -461,6 +461,8 @@ void Creature::setDirection(Otc::Direction direction)
461 461
 
462 462
 void Creature::setOutfit(const Outfit& outfit)
463 463
 {
464
+    if(!g_things.isValidDatId(outfit.getId(), DatCreatureCategory))
465
+        return;
464 466
     m_walkAnimationPhase = 0; // might happen when player is walking and outfit is changed.
465 467
     m_outfit = outfit;
466 468
 }

+ 2
- 0
src/otclient/effect.cpp View File

@@ -46,6 +46,8 @@ void Effect::startAnimation()
46 46
 
47 47
 void Effect::setId(uint32 id)
48 48
 {
49
+    if(!g_things.isValidDatId(id, DatEffectCategory))
50
+        id = 0;
49 51
     m_id = id;
50 52
 }
51 53
 

+ 19
- 31
src/otclient/game.cpp View File

@@ -38,7 +38,7 @@ Game g_game;
38 38
 Game::Game()
39 39
 {
40 40
     resetGameStates();
41
-    setClientVersion(860);
41
+    m_protocolVersion = 0;
42 42
 }
43 43
 
44 44
 void Game::resetGameStates()
@@ -416,10 +416,11 @@ void Game::processWalkCancel(Otc::Direction direction)
416 416
 
417 417
 void Game::loginWorld(const std::string& account, const std::string& password, const std::string& worldName, const std::string& worldHost, int worldPort, const std::string& characterName)
418 418
 {
419
-    if(m_protocolGame || isOnline()) {
420
-        g_logger.traceError("unable to login into a world while already online or logging");
421
-        return;
422
-    }
419
+    if(m_protocolGame || isOnline())
420
+        stdext::throw_exception("Unable to login into a world while already online or logging.");
421
+
422
+    if(m_protocolVersion == 0)
423
+        stdext::throw_exception("Must set a valid game protocol version before logging.");
423 424
 
424 425
     m_protocolGame = ProtocolGamePtr(new ProtocolGame);
425 426
     m_protocolGame->login(account, password, worldHost, (uint16)worldPort, characterName);
@@ -1090,61 +1091,48 @@ bool Game::canPerformGameAction()
1090 1091
     return m_localPlayer && !m_dead && m_protocolGame && m_protocolGame->isConnected() && checkBotProtection();
1091 1092
 }
1092 1093
 
1093
-void Game::setClientVersion(int clientVersion)
1094
+void Game::setProtocolVersion(int version)
1094 1095
 {
1095
-    if(isOnline()) {
1096
-        g_logger.error("Unable to change client version while online");
1097
-        return;
1098
-    }
1096
+    if(isOnline())
1097
+        stdext::throw_exception("Unable to change client version while online");
1099 1098
 
1100
-    //TODO: check supported versions
1099
+    if(version < 810 || version > 960)
1100
+        stdext::throw_exception(stdext::format("Protocol version %d not supported", version));
1101 1101
 
1102 1102
     m_features.reset();
1103 1103
 
1104
-    if(clientVersion >= 854) {
1104
+    if(version >= 854) {
1105 1105
         enableFeature(Otc::GameProtocolChecksum);
1106 1106
         enableFeature(Otc::GameAccountNames);
1107 1107
         enableFeature(Otc::GameChallangeOnLogin);
1108
-        enableFeature(Otc::GameStackposOnTileAddThing);
1109 1108
         enableFeature(Otc::GameDoubleFreeCapacity);
1110
-        enableFeature(Otc::GameCreatureAdditionalInfo);
1111
-        enableFeature(Otc::GameReverseCreatureStack);
1112
-    }
1113
-
1114
-    if(clientVersion >= 860) {
1115
-        enableFeature(Otc::GameIdOnCancelAttack);
1109
+        enableFeature(Otc::GameCreatureEmblems);
1116 1110
     }
1117 1111
 
1118
-    if(clientVersion >= 862) {
1112
+    if(version >= 862) {
1119 1113
         enableFeature(Otc::GamePenalityOnDeath);
1120 1114
     }
1121 1115
 
1122
-    if(clientVersion >= 870) {
1116
+    if(version >= 870) {
1123 1117
         enableFeature(Otc::GameDoubleExperience);
1124 1118
         enableFeature(Otc::GamePlayerMounts);
1125 1119
     }
1126 1120
 
1127
-    if(clientVersion >= 910) {
1121
+    if(version >= 910) {
1128 1122
         enableFeature(Otc::GameNameOnNpcTrade);
1129 1123
         enableFeature(Otc::GameTotalCapacity);
1130 1124
         enableFeature(Otc::GameSkillsBase);
1131
-        enableFeature(Otc::GameAdditionalPlayerStats);
1125
+        enableFeature(Otc::GamePlayerRegenerationTime);
1132 1126
         enableFeature(Otc::GameChannelPlayerList);
1133 1127
         enableFeature(Otc::GameEnvironmentEffect);
1134
-        enableFeature(Otc::GameCreatureType);
1135 1128
         enableFeature(Otc::GameItemAnimationPhase);
1136 1129
     }
1137 1130
 
1138
-    if(clientVersion >= 940) {
1131
+    if(version >= 940) {
1139 1132
         enableFeature(Otc::GamePlayerMarket);
1140 1133
     }
1141 1134
 
1142
-    if(clientVersion >= 953) {
1143
-        enableFeature(Otc::GameCreaturePassableInfo);
1144
-        enableFeature(Otc::GameTrucatedPingOpcode);
1145
-    }
1146
-
1147
-    m_clientVersion = clientVersion;
1135
+    m_protocolVersion = version;
1148 1136
 }
1149 1137
 
1150 1138
 void Game::setAttackingCreature(const CreaturePtr& creature)

+ 3
- 4
src/otclient/game.h View File

@@ -232,8 +232,8 @@ public:
232 232
     void setFeature(Otc::GameFeature feature) { m_features.set(feature, false); }
233 233
     bool getFeature(Otc::GameFeature feature) { return m_features.test(feature); }
234 234
 
235
-    void setClientVersion(int clientVersion);
236
-    int getClientVersion() { return m_clientVersion; }
235
+    void setProtocolVersion(int version);
236
+    int getProtocolVersion() { return m_protocolVersion; }
237 237
 
238 238
     void setRSA(const std::string& rsa);
239 239
     std::string getRSA() { return m_rsa; }
@@ -256,7 +256,6 @@ public:
256 256
     int getServerBeat() { return m_serverBeat; }
257 257
     LocalPlayerPtr getLocalPlayer() { return m_localPlayer; }
258 258
     ProtocolGamePtr getProtocolGame() { return m_protocolGame; }
259
-    int getProtocolVersion() { return PROTOCOL; }
260 259
     std::string getCharacterName() { return m_characterName; }
261 260
     std::string getWorldName() { return m_worldName; }
262 261
     std::vector<uint8> getGMActions() { return m_gmActions; }
@@ -288,7 +287,7 @@ private:
288 287
     std::string m_characterName;
289 288
     std::string m_worldName;
290 289
     std::bitset<Otc::LastGameFeature> m_features;
291
-    int m_clientVersion;
290
+    int m_protocolVersion;
292 291
     std::string m_rsa;
293 292
 };
294 293
 

+ 8
- 3
src/otclient/item.cpp View File

@@ -101,7 +101,7 @@ void Item::draw(const Point& dest, float scaleFactor, bool animate)
101 101
             else if(tile->mustHookEast())
102 102
                 xPattern = getNumPatternX() >= 3 ? 2 : 0;
103 103
         }
104
-    } else if(isFluid() || isFluidContainer()) {
104
+    } else if(isSplash() || isFluidContainer()) {
105 105
         int color = Otc::FluidTransparent;
106 106
         switch(m_countOrSubType) {
107 107
             case Otc::FluidNone:
@@ -176,15 +176,20 @@ void Item::draw(const Point& dest, float scaleFactor, bool animate)
176 176
 
177 177
 void Item::setId(uint32 id)
178 178
 {
179
-    m_otbId = g_things.findOtbForClientId(id)->getServerId();
179
+    if(!g_things.isValidDatId(id, DatItemCategory))
180
+        id = 0;
181
+    //m_otbId = g_things.findOtbForClientId(id)->getServerId();
180 182
     m_id = id;
183
+    m_otbId = 0;
181 184
 }
182 185
 
183 186
 void Item::setOtbId(uint16 id)
184 187
 {
188
+    if(!g_things.isValidOtbId(id))
189
+        id = 0;
185 190
     auto otbType = g_things.getOtbType(id);
186
-    m_otbId = id;
187 191
     m_id = otbType->getClientId();
192
+    m_otbId = id;
188 193
 }
189 194
 
190 195
 bool Item::isValid()

+ 1
- 1
src/otclient/luafunctions.cpp View File

@@ -190,10 +190,10 @@ void OTClient::registerLuaFunctions()
190 190
     g_lua.bindSingletonFunction("g_game", "getLocalPlayer", &Game::getLocalPlayer, &g_game);
191 191
     g_lua.bindSingletonFunction("g_game", "getProtocolGame", &Game::getProtocolGame, &g_game);
192 192
     g_lua.bindSingletonFunction("g_game", "getProtocolVersion", &Game::getProtocolVersion, &g_game);
193
+    g_lua.bindSingletonFunction("g_game", "setProtocolVersion", &Game::setProtocolVersion, &g_game);
193 194
     g_lua.bindSingletonFunction("g_game", "getCharacterName", &Game::getCharacterName, &g_game);
194 195
     g_lua.bindSingletonFunction("g_game", "getWorldName", &Game::getWorldName, &g_game);
195 196
     g_lua.bindSingletonFunction("g_game", "getGMActions", &Game::getGMActions, &g_game);
196
-    g_lua.bindSingletonFunction("g_game", "getClientVersion", &Game::getClientVersion, &g_game);
197 197
     g_lua.bindSingletonFunction("g_game", "getFeature", &Game::getFeature, &g_game);
198 198
 
199 199
     g_lua.registerSingletonClass("g_shaders");

+ 1
- 1
src/otclient/map.cpp View File

@@ -467,7 +467,7 @@ bool Map::loadOtcm(const std::string& fileName)
467 467
                 uint8 countOrSubType = fin->getU8();
468 468
 
469 469
                 ItemPtr item = Item::create(id);
470
-                if(item->isStackable() || item->isFluidContainer() || item->isFluid())
470
+                if(item->isStackable() || item->isFluidContainer() || item->isSplash() || item->isChargeable())
471 471
                     item->setCountOrSubType(countOrSubType);
472 472
 
473 473
                 if(item->isValid())

+ 2
- 0
src/otclient/missile.cpp View File

@@ -83,6 +83,8 @@ void Missile::setPath(const Position& fromPosition, const Position& toPosition)
83 83
 
84 84
 void Missile::setId(uint32 id)
85 85
 {
86
+    if(!g_things.isValidDatId(id, DatMissileCategory))
87
+        id = 0;
86 88
     m_id = id;
87 89
 }
88 90
 

+ 1
- 1
src/otclient/outfit.cpp View File

@@ -25,7 +25,7 @@
25 25
 Outfit::Outfit()
26 26
 {
27 27
     m_category = DatCreatureCategory;
28
-    m_id = 1;
28
+    m_id = 0;
29 29
     resetClothes();
30 30
 }
31 31
 

+ 0
- 42
src/otclient/protocolcodes.h View File

@@ -25,49 +25,7 @@
25 25
 
26 26
 #include "global.h"
27 27
 
28
-#if !(PROTOCOL == 810) && \
29
-    !(PROTOCOL == 854) && \
30
-    !(PROTOCOL >= 860 && PROTOCOL <= 862) && \
31
-    !(PROTOCOL >= 870 && PROTOCOL <= 871) && \
32
-    !(PROTOCOL >= 910 && PROTOCOL <= 953)
33
-#error "the supplied protocol version is not supported"
34
-#endif
35
-
36 28
 namespace Proto {
37
-#ifdef CIPSOFT_RSA
38
-    constexpr const char* RSA = "1321277432058722840622950990822933849527763264961655079678763618"
39
-                          "4334395343554449668205332383339435179772895415509701210392836078"
40
-                          "6959821132214473291575712138800495033169914814069637740318278150"
41
-                          "2907336840325241747827401343576296990629870233111328210165697754"
42
-                          "88792221429527047321331896351555606801473202394175817";
43
-#else
44
-    constexpr const char* RSA = "1091201329673994292788609605089955415282375029027981291234687579"
45
-                          "3726629149257644633073969600111060390723088861007265581882535850"
46
-                          "3429057592827629436413108566029093628212635953836686562675849720"
47
-                          "6207862794310902180176810615217550567108238764764442605581471797"
48
-                          "07119674283982419152118103759076030616683978566631413";
49
-#endif
50
-
51
-    constexpr int PicSignature = 0x4F8C231A; // 953 pic signature
52
-    constexpr int ClientVersion = PROTOCOL;
53
-
54
-    enum OsTypes {
55
-        OsLinux = 1,
56
-        OsWindows = 2,
57
-        OsFlash = 3,
58
-        OsOtclientLinux = 10,
59
-        OsOtclientWindows = 11,
60
-        OsOtclientMac = 12
61
-    };
62
-
63
-#ifdef OSTYPE
64
-    constexpr int ClientOs = OSTYPE;
65
-#elif defined WIN32
66
-    constexpr int ClientOs = OsOtclientWindows;
67
-#else
68
-    constexpr int ClientOs = OsOtclientLinux;
69
-#endif
70
-
71 29
     enum LoginServerOpts {
72 30
         LoginServerError = 10,
73 31
         LoginServerMotd = 20,

+ 21
- 24
src/otclient/protocolgameparse.cpp View File

@@ -43,8 +43,11 @@ void ProtocolGame::parseMessage(const InputMessagePtr& msg)
43 43
             opcode = msg->getU8();
44 44
 
45 45
             // try to parse in lua first
46
+            int readPos = msg->getReadPos();
46 47
             if(callLuaField<bool>("onOpcode", opcode, msg))
47 48
                 continue;
49
+            else
50
+                msg->setReadPos(readPos); // restore read pos
48 51
 
49 52
             if(!m_gameInitialized && opcode > Proto::GameServerFirstGameOpcode)
50 53
                 g_logger.warning("received a game opcode from the server, but the game is not initialized yet, this is a server side bug");
@@ -66,17 +69,13 @@ void ProtocolGame::parseMessage(const InputMessagePtr& msg)
66 69
                 parseLoginWait(msg);
67 70
                 break;
68 71
             case Proto::GameServerPing:
69
-                if(g_game.getFeature(Otc::GameTrucatedPingOpcode))
72
+            case Proto::GameServerPingBack:
73
+                if((opcode == Proto::GameServerPing && g_game.getProtocolVersion() >= 953) ||
74
+                   (opcode == Proto::GameServerPingBack && g_game.getProtocolVersion() < 953))
70 75
                     parsePingBack(msg);
71 76
                 else
72 77
                     parsePing(msg);
73 78
                 break;
74
-            case Proto::GameServerPingBack:
75
-                if(g_game.getFeature(Otc::GameTrucatedPingOpcode))
76
-                    parsePing(msg);
77
-                else
78
-                    parsePingBack(msg);
79
-                break;
80 79
             case Proto::GameServerChallange:
81 80
                 parseChallange(msg);
82 81
                 break;
@@ -334,9 +333,9 @@ void ProtocolGame::parseGMActions(const InputMessagePtr& msg)
334 333
 
335 334
     int numViolationReasons;
336 335
 
337
-    if(g_game.getClientVersion() >= 860)
336
+    if(g_game.getProtocolVersion() >= 860)
338 337
         numViolationReasons = 20;
339
-    else if(g_game.getClientVersion() >= 854)
338
+    else if(g_game.getProtocolVersion() >= 854)
340 339
         numViolationReasons = 19;
341 340
     else
342 341
         numViolationReasons = 32;
@@ -454,7 +453,7 @@ void ProtocolGame::parseTileAddThing(const InputMessagePtr& msg)
454 453
     Position pos = getPosition(msg);
455 454
     int stackPos = -1;
456 455
 
457
-    if(g_game.getFeature(Otc::GameStackposOnTileAddThing))
456
+    if(g_game.getProtocolVersion() >= 854)
458 457
         stackPos = msg->getU8();
459 458
 
460 459
     ThingPtr thing = getThing(msg);
@@ -508,7 +507,7 @@ void ProtocolGame::parseCreatureMove(const InputMessagePtr& msg)
508 507
     int stackPos = -2;
509 508
 
510 509
     // older protocols stores creatures in reverse order
511
-    if(!g_game.getFeature(Otc::GameReverseCreatureStack))
510
+    if(!g_game.getProtocolVersion() >= 854)
512 511
         stackPos = -1;
513 512
 
514 513
     g_map.addThing(thing, newPos, stackPos);
@@ -884,12 +883,11 @@ void ProtocolGame::parsePlayerStats(const InputMessagePtr& msg)
884 883
     m_localPlayer->setStamina(stamina);
885 884
     m_localPlayer->setSoul(soul);
886 885
 
887
-    if(g_game.getFeature(Otc::GameAdditionalPlayerStats)) {
888
-        int speed = msg->getU16();
889
-        msg->getU16(); // regeneration time
886
+    if(g_game.getProtocolVersion() >= 910)
887
+        m_localPlayer->setSpeed(msg->getU16());
890 888
 
891
-        m_localPlayer->setSpeed(speed);
892
-    }
889
+    if(g_game.getFeature(Otc::GamePlayerRegenerationTime))
890
+        msg->getU16(); // regeneration time
893 891
 }
894 892
 
895 893
 void ProtocolGame::parsePlayerSkills(const InputMessagePtr& msg)
@@ -913,7 +911,7 @@ void ProtocolGame::parsePlayerState(const InputMessagePtr& msg)
913 911
 
914 912
 void ProtocolGame::parsePlayerCancelAttack(const InputMessagePtr& msg)
915 913
 {
916
-    if(g_game.getFeature(Otc::GameIdOnCancelAttack))
914
+    if(g_game.getProtocolVersion() >= 860)
917 915
         msg->getU32(); // unknown
918 916
 
919 917
     g_game.processAttackCancel();
@@ -1389,7 +1387,7 @@ CreaturePtr ProtocolGame::getCreature(const InputMessagePtr& msg, int type)
1389 1387
             uint id = msg->getU32();
1390 1388
 
1391 1389
             int creatureType;
1392
-            if(g_game.getFeature(Otc::GameCreatureType))
1390
+            if(g_game.getProtocolVersion() >= 910)
1393 1391
                 creatureType = msg->getU8();
1394 1392
             else {
1395 1393
                 if(id >= Proto::PlayerStartId && id < Proto::PlayerEndId)
@@ -1441,12 +1439,11 @@ CreaturePtr ProtocolGame::getCreature(const InputMessagePtr& msg, int type)
1441 1439
         int emblem = -1;
1442 1440
         bool passable = false;
1443 1441
 
1444
-        if(g_game.getFeature(Otc::GameCreatureAdditionalInfo)) {
1445
-            if(!known)
1446
-                emblem = msg->getU8();
1442
+        if(g_game.getFeature(Otc::GameCreatureEmblems) && !known)
1443
+            emblem = msg->getU8();
1447 1444
 
1445
+        if(g_game.getProtocolVersion() >= 854)
1448 1446
             passable = (msg->getU8() == 0);
1449
-        }
1450 1447
 
1451 1448
         if(creature) {
1452 1449
             creature->setHealthPercent(healthPercent);
@@ -1474,7 +1471,7 @@ CreaturePtr ProtocolGame::getCreature(const InputMessagePtr& msg, int type)
1474 1471
         if(creature)
1475 1472
             creature->turn(direction);
1476 1473
 
1477
-        if(g_game.getFeature(Otc::GameCreaturePassableInfo)) {
1474
+        if(g_game.getProtocolVersion() >= 953) {
1478 1475
             bool passable = msg->getU8();
1479 1476
 
1480 1477
             if(creature)
@@ -1497,7 +1494,7 @@ ItemPtr ProtocolGame::getItem(const InputMessagePtr& msg, int id)
1497 1494
     if(item->getId() == 0)
1498 1495
         stdext::throw_exception("unable to create item with invalid id 0");
1499 1496
 
1500
-    if(item->isStackable() || item->isFluidContainer() || item->isFluid())
1497
+    if(item->isStackable() || item->isFluidContainer() || item->isSplash() || item->isChargeable())
1501 1498
         item->setCountOrSubType(msg->getU8());
1502 1499
 
1503 1500
     if(g_game.getFeature(Otc::GameItemAnimationPhase)) {

+ 8
- 6
src/otclient/protocolgamesend.cpp View File

@@ -49,8 +49,9 @@ void ProtocolGame::sendLoginPacket(uint challangeTimestamp, uint8 challangeRando
49 49
     OutputMessagePtr msg(new OutputMessage);
50 50
 
51 51
     msg->addU8(Proto::ClientEnterGame);
52
-    msg->addU16(Proto::ClientOs);
53
-    msg->addU16(g_game.getClientVersion());
52
+
53
+    msg->addU16(g_lua.callGlobalField<int>("g_game", "getOs"));
54
+    msg->addU16(g_game.getProtocolVersion());
54 55
 
55 56
     int paddingBytes = 128;
56 57
     msg->addU8(0); // first RSA byte must be 0
@@ -90,7 +91,7 @@ void ProtocolGame::sendLoginPacket(uint challangeTimestamp, uint8 challangeRando
90 91
     msg->addPaddingBytes(paddingBytes);
91 92
 
92 93
     // encrypt with RSA
93
-    msg->encryptRSA(128, Proto::RSA);
94
+    msg->encryptRsa(128, g_lua.callGlobalField<std::string>("g_game", "getRsa"));
94 95
 
95 96
     send(msg);
96 97
 
@@ -570,9 +571,10 @@ void ProtocolGame::sendShareExperience(bool active, int unknown)
570 571
     OutputMessagePtr msg(new OutputMessage);
571 572
     msg->addU8(Proto::ClientShareExperience);
572 573
     msg->addU8(active ? 0x01 : 0x00);
573
-#if PROTOCOL<910
574
-    msg->addU8(unknown);
575
-#endif
574
+
575
+    if(g_game.getProtocolVersion() < 910)
576
+        msg->addU8(unknown);
577
+
576 578
     send(msg);
577 579
 }
578 580
 

+ 4
- 9
src/otclient/thing.h View File

@@ -28,12 +28,6 @@
28 28
 #include "thingtypemanager.h"
29 29
 #include <framework/luaengine/luaobject.h>
30 30
 
31
-struct Light
32
-{
33
-    uint8 intensity;
34
-    uint8 color;
35
-};
36
-
37 31
 // @bindclass
38 32
 #pragma pack(push,1) // disable memory alignment
39 33
 class Thing : public LuaObject
@@ -95,8 +89,7 @@ public:
95 89
     int getAnimationPhases() { return rawGetDatType()->getAnimationPhases(); }
96 90
     int getGroundSpeed() { return rawGetDatType()->getGroundSpeed(); }
97 91
     int getMaxTextLength() { return rawGetDatType()->getMaxTextLength(); }
98
-    int getLightLevel() { return rawGetDatType()->getLightLevel(); }
99
-    int getLightColor() { return rawGetDatType()->getLightColor(); }
92
+    Light getLight() { return rawGetDatType()->getLight(); }
100 93
     int getMinimapColor() { return rawGetDatType()->getMinimapColor(); }
101 94
     int getLensHelp() { return rawGetDatType()->getLensHelp(); }
102 95
     int getClothSlot() { return rawGetDatType()->getClothSlot(); }
@@ -110,9 +103,10 @@ public:
110 103
     bool isForceUse() { return rawGetDatType()->isForceUse(); }
111 104
     bool isMultiUse() { return rawGetDatType()->isMultiUse(); }
112 105
     bool isWritable() { return rawGetDatType()->isWritable(); }
106
+    bool isChargeable() { return rawGetDatType()->isChargeable(); }
113 107
     bool isWritableOnce() { return rawGetDatType()->isWritableOnce(); }
114 108
     bool isFluidContainer() { return rawGetDatType()->isFluidContainer(); }
115
-    bool isFluid() { return rawGetDatType()->isFluid(); }
109
+    bool isSplash() { return rawGetDatType()->isSplash(); }
116 110
     bool isNotWalkable() { return rawGetDatType()->isNotWalkable(); }
117 111
     bool isNotMoveable() { return rawGetDatType()->isNotMoveable(); }
118 112
     bool blockProjectile() { return rawGetDatType()->blockProjectile(); }
@@ -134,6 +128,7 @@ public:
134 128
     bool isFullGround() { return rawGetDatType()->isFullGround(); }
135 129
     bool isIgnoreLook() { return rawGetDatType()->isIgnoreLook(); }
136 130
     bool isCloth() { return rawGetDatType()->isCloth(); }
131
+    MarketData getMarketData() { return rawGetDatType()->getMarketData(); }
137 132
 
138 133
 protected:
139 134
     Position m_position;

+ 64
- 162
src/otclient/thingtypedat.cpp View File

@@ -22,6 +22,7 @@
22 22
 
23 23
 #include "thingtypedat.h"
24 24
 #include "spritemanager.h"
25
+#include "game.h"
25 26
 
26 27
 #include <framework/graphics/graphics.h>
27 28
 #include <framework/graphics/texture.h>
@@ -33,20 +34,11 @@ ThingTypeDat::ThingTypeDat()
33 34
 {
34 35
     m_category = DatInvalidCategory;
35 36
     m_id = 0;
37
+    m_null = true;
36 38
     m_exactSize = 0;
37
-    m_layers = 0;
38
-    m_numPatternX = 0;
39
-    m_numPatternY = 0;
40
-    m_numPatternZ = 0;
39
+    m_numPatternX = m_numPatternY = m_numPatternZ = 0;
41 40
     m_animationPhases = 0;
42
-    m_groundSpeed = 0;
43
-    m_maxTextLenght = 0;
44
-    m_lightLevel = 0;
45
-    m_lightColor = 0;
46
-    m_miniMapColor = 0;
47
-    m_lensHelp = 0;
48
-    m_clothSlot = 0;
49
-    m_elevation = 0;
41
+    m_layers = 0;
50 42
 }
51 43
 
52 44
 void ThingTypeDat::unserialize(uint16 clientId, DatCategory category, const FileStreamPtr& fin)
@@ -55,136 +47,68 @@ void ThingTypeDat::unserialize(uint16 clientId, DatCategory category, const File
55 47
     m_id = clientId;
56 48
     m_category = category;
57 49
 
50
+
51
+    static int datVersion;
52
+    if(clientId == 100 && category == DatItemCategory)
53
+        datVersion = 2;
54
+
58 55
     bool done = false;
59 56
     for(int i = 0 ; i < DatLastAttrib;++i) {
60
-        int property = fin->getU8();
61
-        if(property == DatLastAttrib) {
57
+        int attrib = fin->getU8();
58
+        if(attrib == DatLastAttrib) {
62 59
             done = true;
63 60
             break;
64 61
         }
65 62
 
66
-        switch(property) {
67
-            case DatAttribIsGround:
68
-                m_isGround = true;
69
-                m_groundSpeed = fin->getU16();
70
-                if(m_groundSpeed == 0)
71
-                    m_groundSpeed = 100;
72
-                break;
73
-            case DatAttribIsGroundBorder:
74
-                m_isGroundBorder = true;
75
-                break;
76
-            case DatAttribIsOnBottom:
77
-                m_isOnBottom = true;
78
-                break;
79
-            case DatAttribIsOnTop:
80
-                m_isOnTop = true;
81
-                break;
82
-            case DatAttribIsContainer:
83
-                m_isContainer = true;
84
-                break;
85
-            case DatAttribIsStackable:
86
-                m_isStackable = true;
87
-                break;
88
-            case DatAttribIsForceUse:
89
-                m_isForceUse = true;
90
-                break;
91
-            case DatAttribIsMultiUse:
92
-                m_isMultiUse = true;
93
-                break;
94
-            case DatAttribIsWritable:
95
-                m_isWritable = true;
96
-                m_maxTextLenght = fin->getU16();
97
-                break;
98
-            case DatAttribIsWritableOnce:
99
-                m_isWritableOnce = true;
100
-                m_maxTextLenght = fin->getU16();
101
-                break;
102
-            case DatAttribIsFluidContainer:
103
-                m_isFluidContainer = true;
104
-                break;
105
-            case DatAttribIsFluid:
106
-                m_isFluid = true;
107
-                break;
108
-            case DatAttribIsNotWalkable:
109
-                m_isNotWalkable = true;
110
-                break;
111
-            case DatAttribIsNotMoveable:
112
-                m_isNotMoveable = true;
113
-                break;
114
-            case DatAttribBlockProjectile:
115
-                m_blockProjectile = true;
116
-                break;
117
-            case DatAttribIsNotPathable:
118
-                m_isNotPathable = true;
119
-                break;
120
-            case DatAttribIsPickupable:
121
-                m_isPickupable = true;
122
-                break;
123
-            case DatAttribIsHangable:
124
-                m_isHangable = true;
125
-                break;
126
-            case DatAttribHookSouth:
127
-                m_isHookSouth = true;
128
-                break;
129
-            case DatAttribHookEast:
130
-                m_isHookEast = true;
131
-                break;
132
-            case DatAttribIsRotateable:
133
-                m_isRotateable = true;
134
-                break;
135
-            case DatAttribHasLight:
136
-                m_hasLight = true;
137
-                m_lightLevel = fin->getU16();
138
-                m_lightColor = fin->getU16();
139
-                break;
140
-            case DatAttribDontHide:
141
-                m_isDontHide = true;
142
-                break;
143
-            case DatAttribIsTranslucent:
144
-                m_isTranslucent = true;
145
-                break;
146
-            case DatAttribHasDisplacement:
147
-                m_hasDisplacement = true;
148
-                m_displacement = Point(fin->getU16(), fin->getU16());
149
-                break;
150
-            case DatAttribHasElevation:
151
-                m_hasElevation = true;
152
-                m_elevation = fin->getU16();
63
+        // hacky way to detect if is older dat version or not
64
+        if(clientId == 100 && category == DatItemCategory && datVersion != 1 &&
65
+           (attrib == DatAttribNotPathable || attrib == DatAttribDontHide || attrib == DatAttribIgnoreLook)) {
66
+            datVersion = 1;
67
+        }
68
+        if(datVersion <= 1) {
69
+            if(attrib == DatAttribWritable) {
70
+                m_attribs.set(DatAttribChargeable, true);
71
+                continue;
72
+            } else if(attrib > DatAttribWritable)
73
+                attrib -= 1;
74
+        }
75
+
76
+        switch(attrib) {
77
+            case DatAttribDisplacement: {
78
+                m_displacement.x = fin->getU16();
79
+                m_displacement.y = fin->getU16();
80
+                m_attribs.set(attrib, true);
153 81
                 break;
154
-            case DatAttribIsLyingCorpse:
155
-                m_isLyingCorpse = true;
82
+            }
83
+            case DatAttribLight: {
84
+                Light light;
85
+                light.intensity = fin->getU16();
86
+                light.color = fin->getU16();
87
+                m_attribs.set(attrib, light);
156 88
                 break;
157
-            case DatAttribAnimateAlways:
158
-                m_isAnimateAlways = true;
89
+            }
90
+            case DatAttribMarket: {
91
+                MarketData market;
92
+                market.category = fin->getU16();
93
+                market.showAs = fin->getU16();
94
+                market.tradeAs = fin->getU16();
95
+                market.name = fin->getString();
96
+                market.restrictProfession = fin->getU16();
97
+                market.requiredLevel = fin->getU16();
98
+                m_attribs.set(attrib, market);
159 99
                 break;
100
+            }
101
+            case DatAttribGround:
102
+            case DatAttribWritable:
103
+            case DatAttribWritableOnce:
104
+            case DatAttribElevation:
160 105
             case DatAttribMiniMapColor:
161
-                m_miniMapColor = true;
162
-                m_miniMapColor = fin->getU16();
163
-                break;
164
-            case DatAttribLensHelp:
165
-                m_lensHelp = true;
166
-                m_lensHelp = fin->getU16();
167
-                break;
168
-            case DatAttribIsFullGround:
169
-                m_isFullGround = true;
170
-                break;
171
-            case DatAttribIgnoreLook:
172
-                m_isIgnoreLook = true;
173
-                break;
174 106
             case DatAttribCloth:
175
-                m_isCloth = true;
176
-                m_clothSlot = fin->getU16();
177
-                break;
178
-            case DatAttribMarket:
179
-                fin->getU16(); // category
180
-                fin->getU16(); // trade as
181
-                fin->getU16(); // show as
182
-                fin->getString(); // name
183
-                fin->getU16(); // restrict profession
184
-                fin->getU16(); // level
107
+            case DatAttribLensHelp:
108
+                m_attribs.set(attrib, fin->getU16());
185 109
                 break;
186 110
             default:
187
-                stdext::throw_exception("corrupt data, invalid type attribute");
111
+                m_attribs.set(attrib, true);
188 112
                 break;
189 113
         };
190 114
     }
@@ -192,40 +116,18 @@ void ThingTypeDat::unserialize(uint16 clientId, DatCategory category, const File
192 116
     if(!done)
193 117
         stdext::throw_exception("corrupt data");
194 118
 
195
-    int totalSprites = 1;
196
-    for(int i = 0; i < DatLastDimension; ++i) {
197
-        switch(i) {
198
-            case DatWidth:
199
-                m_size.setWidth(fin->getU8());
200
-                break;
201
-            case DatHeight:
202
-                m_size.setHeight(fin->getU8());
203
-                break;
204
-            case DatExactSize:
205
-                if(m_size.width() <= 1 && m_size.height() <= 1)
206
-                    m_exactSize = 32;
207
-                else
208
-                    m_exactSize = std::min((int)fin->getU8(), std::max(m_size.width() * 32, m_size.height() * 32));
209
-                break;
210
-            case DatLayers:
211
-                m_layers = fin->getU8();
212
-                break;
213
-            case DatPatternX:
214
-                m_numPatternX = fin->getU8();
215
-                break;
216
-            case DatPatternY:
217
-                m_numPatternY = fin->getU8();
218
-                break;
219
-            case DatPatternZ:
220
-                m_numPatternZ = fin->getU8();
221
-                break;
222
-            case DatAnimationPhases:
223
-                m_animationPhases = fin->getU8();
224
-                break;
225
-        }
226
-    }
119
+    uint8 width = fin->getU8();
120
+    uint8 height = fin->getU8();
121
+    m_size = Size(width, height);
122
+    m_exactSize = (width > 1 || height > 1) ? std::min((int)fin->getU8(), std::max(width * 32, height * 32)) : 32;
123
+    m_layers = fin->getU8();
124
+    m_numPatternX = fin->getU8();
125
+    m_numPatternY = fin->getU8();
126
+    m_numPatternZ = fin->getU8();
127
+    m_animationPhases = fin->getU8();
128
+
129
+    int totalSprites = m_size.area() * m_layers * m_numPatternX * m_numPatternY * m_numPatternZ * m_animationPhases;
227 130
 
228
-    totalSprites = m_size.width() * m_size.height() * m_layers * m_numPatternX * m_numPatternY * m_numPatternZ * m_animationPhases;
229 131
     if(totalSprites == 0)
230 132
         stdext::throw_exception("a thing type has no sprites");
231 133
     if(totalSprites > 4096)

+ 97
- 131
src/otclient/thingtypedat.h View File

@@ -29,6 +29,7 @@
29 29
 #include <framework/graphics/coordsbuffer.h>
30 30
 #include <framework/luaengine/luaobject.h>
31 31
 #include <framework/net/server.h>
32
+#include <framework/util/attribstorage.h>
32 33
 
33 34
 enum DatCategory {
34 35
     DatItemCategory = 0,
@@ -48,53 +49,59 @@ enum DatSpriteMask {
48 49
 };
49 50
 
50 51
 enum DatAttrib {
51
-    DatAttribIsGround = 0,
52
-    DatAttribIsGroundBorder,
53
-    DatAttribIsOnBottom,
54
-    DatAttribIsOnTop,
55
-    DatAttribIsContainer,
56
-    DatAttribIsStackable,
57
-    DatAttribIsForceUse,
58
-    DatAttribIsMultiUse,
59
-    DatAttribIsWritable,
60
-    DatAttribIsWritableOnce,
61
-    DatAttribIsFluidContainer,
62
-    DatAttribIsFluid,
63
-    DatAttribIsNotWalkable,
64
-    DatAttribIsNotMoveable,
52
+    DatAttribGround = 0,
53
+    DatAttribGroundBorder,
54
+    DatAttribOnBottom,
55
+    DatAttribOnTop,
56
+    DatAttribContainer,
57
+    DatAttribStackable,
58
+    DatAttribForceUse,
59
+    DatAttribMultiUse,
60
+    //DatAttribRune
61
+    DatAttribWritable,
62
+    DatAttribWritableOnce,
63
+    DatAttribFluidContainer,
64
+    DatAttribSplash,
65
+    DatAttribNotWalkable,
66
+    DatAttribNotMoveable,
65 67
     DatAttribBlockProjectile,
66
-    DatAttribIsNotPathable,
67
-    DatAttribIsPickupable,
68
-    DatAttribIsHangable,
68
+    DatAttribNotPathable,
69
+    DatAttribPickupable,
70
+    DatAttribHangable,
69 71
     DatAttribHookSouth,
70 72
     DatAttribHookEast,
71
-    DatAttribIsRotateable,
72
-    DatAttribHasLight,
73
+    DatAttribRotateable,
74
+    DatAttribLight,
73 75
     DatAttribDontHide,
74
-    DatAttribIsTranslucent,
75
-    DatAttribHasDisplacement,
76
-    DatAttribHasElevation,
77
-    DatAttribIsLyingCorpse,
76
+    DatAttribTranslucent,
77
+    DatAttribDisplacement,
78
+    DatAttribElevation,
79
+    DatAttribLyingCorpse,
78 80
     DatAttribAnimateAlways,
79 81
     DatAttribMiniMapColor,
80 82
     DatAttribLensHelp,
81
-    DatAttribIsFullGround,
83
+    DatAttribFullGround,
82 84
     DatAttribIgnoreLook,
83 85
     DatAttribCloth,
84 86
     DatAttribMarket,
85
-    DatLastAttrib = 255
87
+    DatLastAttrib = 255,
88
+
89
+    // legacy attribs
90
+    DatAttribChargeable = 254
91
+};
92
+
93
+struct MarketData {
94
+    std::string name;
95
+    int category;
96
+    uint16 requiredLevel;
97
+    uint16 restrictProfession;
98
+    uint16 showAs;
99
+    uint16 tradeAs;
86 100
 };
87 101
 
88
-enum DatDimension {
89
-    DatWidth = 0,
90
-    DatHeight,
91
-    DatExactSize,
92
-    DatLayers,
93
-    DatPatternX,
94
-    DatPatternY,
95
-    DatPatternZ,
96
-    DatAnimationPhases,
97
-    DatLastDimension
102
+struct Light {
103
+    uint8 intensity;
104
+    uint8 color;
98 105
 };
99 106
 
100 107
 class ThingTypeDat : public LuaObject
@@ -113,56 +120,58 @@ public:
113 120
     Size getSize() { return m_size; }
114 121
     int getWidth() { return m_size.width(); }
115 122
     int getHeight() { return m_size.height(); }
116
-    Point getDisplacement() { return m_displacement; }
117
-    int getDisplacementX() { return m_displacement.x; }
118
-    int getDisplacementY() { return m_displacement.y; }
119 123
     int getExactSize() { return m_exactSize; }
120 124
     int getLayers() { return m_layers; }
121 125
     int getNumPatternX() { return m_numPatternX; }
122 126
     int getNumPatternY() { return m_numPatternY; }
123 127
     int getNumPatternZ() { return m_numPatternZ; }
124 128
     int getAnimationPhases() { return m_animationPhases; }
125
-    int getGroundSpeed() { return m_groundSpeed; }
126
-    int getMaxTextLength() { return m_maxTextLenght; }
127
-    int getLightLevel() { return m_lightLevel; }
128
-    int getLightColor() { return m_lightColor; }
129
-    int getMinimapColor() { return m_miniMapColor; }
130
-    int getLensHelp() { return m_lensHelp; }
131
-    int getClothSlot() { return m_clothSlot; }
132
-    int getElevation() { return m_elevation; }
133
-    bool isGround() { return m_isGround; }
134
-    bool isGroundBorder() { return m_isGroundBorder; }
135
-    bool isOnBottom() { return m_isOnBottom; }
136
-    bool isOnTop() { return m_isOnTop; }
137
-    bool isContainer() { return m_isContainer; }
138
-    bool isStackable() { return m_isStackable; }
139
-    bool isForceUse() { return m_isForceUse; }
140
-    bool isMultiUse() { return m_isMultiUse; }
141
-    bool isWritable() { return m_isWritable; }
142
-    bool isWritableOnce() { return m_isWritableOnce; }
143
-    bool isFluidContainer() { return m_isFluidContainer; }
144
-    bool isFluid() { return m_isFluid; }
145
-    bool isNotWalkable() { return m_isNotWalkable; }
146
-    bool isNotMoveable() { return m_isNotMoveable; }
147
-    bool blockProjectile() { return m_blockProjectile; }
148
-    bool isNotPathable() { return m_isNotPathable; }
149
-    bool isPickupable() { return m_isPickupable; }
150
-    bool isHangable() { return m_isHangable; }
151
-    bool isHookSouth() { return m_isHookSouth; }
152
-    bool isHookEast() { return m_isHookEast; }
153
-    bool isRotateable() { return m_isRotateable; }
154
-    bool hasLight() { return m_hasLight; }
155
-    bool isDontHide() { return m_isDontHide; }
156
-    bool isTranslucent() { return m_isTranslucent; }
157
-    bool hasDisplacement() { return m_hasDisplacement; }
158
-    bool hasElevation() { return m_hasElevation; }
159
-    bool isLyingCorpse() { return m_isLyingCorpse; }
160
-    bool isAnimateAlways() { return m_isAnimateAlways; }
161
-    bool hasMiniMapColor() { return m_hasMiniMapColor; }
162
-    bool hasLensHelp() { return m_hasLensHelp; }
163
-    bool isFullGround() { return m_isFullGround; }
164
-    bool isIgnoreLook() { return m_isIgnoreLook; }
165
-    bool isCloth() { return m_isCloth; }
129
+    Point getDisplacement() { return m_displacement; }
130
+    int getDisplacementX() { return getDisplacement().x; }
131
+    int getDisplacementY() { return getDisplacement().y; }
132
+
133
+    int getGroundSpeed() { return m_attribs.get<uint16>(DatAttribGround); }
134
+    int getMaxTextLength() { return m_attribs.has(DatAttribWritableOnce) ? m_attribs.get<uint16>(DatAttribWritableOnce) : m_attribs.get<uint16>(DatAttribWritable); }
135
+    Light getLight() { return m_attribs.get<Light>(DatAttribLight); }
136
+    int getMinimapColor() { return m_attribs.get<uint16>(DatAttribMiniMapColor); }
137
+    int getLensHelp() { return m_attribs.get<uint16>(DatAttribLensHelp); }
138
+    int getClothSlot() { return m_attribs.get<uint16>(DatAttribCloth); }
139
+    int getElevation() { return m_attribs.get<uint16>(DatAttribElevation); }
140
+    MarketData getMarketData() { return m_attribs.get<MarketData>(DatAttribMarket); }
141
+    bool isGround() { return m_attribs.has(DatAttribGround); }
142
+    bool isGroundBorder() { return m_attribs.has(DatAttribGroundBorder); }
143
+    bool isOnBottom() { return m_attribs.has(DatAttribOnBottom); }
144
+    bool isOnTop() { return m_attribs.has(DatAttribOnTop); }
145
+    bool isContainer() { return m_attribs.has(DatAttribContainer); }
146
+    bool isStackable() { return m_attribs.has(DatAttribStackable); }
147
+    bool isForceUse() { return m_attribs.has(DatAttribForceUse); }
148
+    bool isMultiUse() { return m_attribs.has(DatAttribMultiUse); }
149
+    bool isWritable() { return m_attribs.has(DatAttribWritable); }
150
+    bool isChargeable() { return m_attribs.has(DatAttribChargeable); }
151
+    bool isWritableOnce() { return m_attribs.has(DatAttribWritableOnce); }
152
+    bool isFluidContainer() { return m_attribs.has(DatAttribFluidContainer); }
153
+    bool isSplash() { return m_attribs.has(DatAttribSplash); }
154
+    bool isNotWalkable() { return m_attribs.has(DatAttribNotWalkable); }
155
+    bool isNotMoveable() { return m_attribs.has(DatAttribNotMoveable); }
156
+    bool blockProjectile() { return m_attribs.has(DatAttribBlockProjectile); }
157
+    bool isNotPathable() { return m_attribs.has(DatAttribNotPathable); }
158
+    bool isPickupable() { return m_attribs.has(DatAttribPickupable); }
159
+    bool isHangable() { return m_attribs.has(DatAttribHangable); }
160
+    bool isHookSouth() { return m_attribs.has(DatAttribHookSouth); }
161
+    bool isHookEast() { return m_attribs.has(DatAttribHookEast); }
162
+    bool isRotateable() { return m_attribs.has(DatAttribRotateable); }
163
+    bool hasLight() { return m_attribs.has(DatAttribLight); }
164
+    bool isDontHide() { return m_attribs.has(DatAttribDontHide); }
165
+    bool isTranslucent() { return m_attribs.has(DatAttribTranslucent); }
166
+    bool hasDisplacement() { return m_attribs.has(DatAttribDisplacement); }
167
+    bool hasElevation() { return m_attribs.has(DatAttribElevation); }
168
+    bool isLyingCorpse() { return m_attribs.has(DatAttribLyingCorpse); }
169
+    bool isAnimateAlways() { return m_attribs.has(DatAttribAnimateAlways); }
170
+    bool hasMiniMapColor() { return m_attribs.has(DatAttribMiniMapColor); }
171
+    bool hasLensHelp() { return m_attribs.has(DatAttribLensHelp); }
172
+    bool isFullGround() { return m_attribs.has(DatAttribFullGround); }
173
+    bool isIgnoreLook() { return m_attribs.has(DatAttribIgnoreLook); }
174
+    bool isCloth() { return m_attribs.has(DatAttribCloth); }
166 175
 
167 176
 private:
168 177
     const TexturePtr& getTexture(int animationPhase);
@@ -172,64 +181,21 @@ private:
172 181
 
173 182
     DatCategory m_category;
174 183
     uint16 m_id;
175
-    Boolean<true> m_null;
184
+    bool m_null;
185
+    AttribStorage m_attribs;
186
+
187
+    Size m_size;
188
+    Point m_displacement;
189
+    int m_exactSize;
190
+    int m_numPatternX, m_numPatternY, m_numPatternZ;
191
+    int m_animationPhases;
192
+    int m_layers;
176 193
 
177 194
     std::vector<int> m_spritesIndex;
178 195
     std::vector<TexturePtr> m_textures;
179 196
     std::vector<std::vector<Rect>> m_texturesFramesRects;
180 197
     std::vector<std::vector<Rect>> m_texturesFramesOriginRects;
181 198
     std::vector<std::vector<Point>> m_texturesFramesOffsets;
182
-
183
-    // dat stuff
184
-    Size m_size;
185
-    Point m_displacement;
186
-    int m_exactSize;
187
-    int m_layers;
188
-    int m_numPatternX;
189
-    int m_numPatternY;
190
-    int m_numPatternZ;
191
-    int m_animationPhases;
192
-    int m_groundSpeed;
193
-    int m_maxTextLenght;
194
-    int m_lightLevel;
195
-    int m_lightColor;
196
-    int m_miniMapColor;
197
-    int m_lensHelp;
198
-    int m_clothSlot;
199
-    int m_elevation;
200
-    Boolean<false> m_isGround;
201
-    Boolean<false> m_isGroundBorder;
202
-    Boolean<false> m_isOnBottom;
203
-    Boolean<false> m_isOnTop;
204
-    Boolean<false> m_isContainer;
205
-    Boolean<false> m_isStackable;
206
-    Boolean<false> m_isForceUse;
207
-    Boolean<false> m_isMultiUse;
208
-    Boolean<false> m_isWritable;
209
-    Boolean<false> m_isWritableOnce;
210
-    Boolean<false> m_isFluidContainer;
211
-    Boolean<false> m_isFluid;
212
-    Boolean<false> m_isNotWalkable;
213
-    Boolean<false> m_isNotMoveable;
214
-    Boolean<false> m_blockProjectile;
215
-    Boolean<false> m_isNotPathable;
216
-    Boolean<false> m_isPickupable;
217
-    Boolean<false> m_isHangable;
218
-    Boolean<false> m_isHookSouth;
219
-    Boolean<false> m_isHookEast;
220
-    Boolean<false> m_isRotateable;
221
-    Boolean<false> m_hasLight;
222
-    Boolean<false> m_isDontHide;
223
-    Boolean<false> m_isTranslucent;
224
-    Boolean<false> m_hasDisplacement;
225
-    Boolean<false> m_hasElevation;
226
-    Boolean<false> m_isLyingCorpse;
227
-    Boolean<false> m_isAnimateAlways;
228
-    Boolean<false> m_hasMiniMapColor;
229
-    Boolean<false> m_hasLensHelp;
230
-    Boolean<false> m_isFullGround;
231
-    Boolean<false> m_isIgnoreLook;
232
-    Boolean<false> m_isCloth;
233 199
 };
234 200
 
235 201
 #endif

+ 1
- 1
src/otclient/tile.cpp View File

@@ -367,7 +367,7 @@ ThingPtr Tile::getTopMultiUseThing()
367 367
     for(uint i = 0; i < m_things.size(); ++i) {
368 368
         ThingPtr thing = m_things[i];
369 369
         if(thing->isForceUse() || (!thing->isGround() && !thing->isGroundBorder() && !thing->isOnBottom() && !thing->isOnTop())) {
370
-            if(i > 0 && thing->isFluid())
370
+            if(i > 0 && thing->isSplash())
371 371
                 return m_things[i-1];
372 372
             return thing;
373 373
         }

+ 1
- 1
src/otclient/uiitem.cpp View File

@@ -58,7 +58,7 @@ void UIItem::drawSelf(Fw::DrawPane drawPane)
58 58
         g_painter->setColor(Color::white);
59 59
         m_item->draw(dest, scaleFactor, true);
60 60
 
61
-        if(m_font && m_item->isStackable() && m_item->getCount() > 1) {
61
+        if(m_font && (m_item->isStackable() || m_item->isChargeable()) && m_item->getCount() > 1) {
62 62
             std::string count = stdext::to_string(m_item->getCount());
63 63
             g_painter->setColor(Color(231, 231, 231));
64 64
             m_font->drawText(count, Rect(m_rect.topLeft(), m_rect.bottomRight() - Point(3, 0)), Fw::AlignBottomRight);

Loading…
Cancel
Save