diff --git a/init.lua b/init.lua index ae2c3caf..1cf988e2 100644 --- a/init.lua +++ b/init.lua @@ -29,17 +29,18 @@ g_configs.load("/config.otml") g_modules.discoverModules() --- core modules 0-99 -g_modules.autoLoadModules(99) +-- libraries modules 0-99 +g_modules.autoLoadModules(99); g_modules.ensureModuleLoaded("corelib") +g_modules.ensureModuleLoaded("gamelib") -- client modules 100-499 g_modules.autoLoadModules(499) g_modules.ensureModuleLoaded("client") -- game modules 500-999 -g_modules.autoLoadModules(999) -g_modules.ensureModuleLoaded("game") +g_modules.autoLoadModules(999); +g_modules.ensureModuleLoaded("game_interface") -- mods 1000-9999 g_modules.autoLoadModules(9999) diff --git a/modules/client/client.otmod b/modules/client/client.otmod index 0217272b..f2ee64b9 100644 --- a/modules/client/client.otmod +++ b/modules/client/client.otmod @@ -16,7 +16,6 @@ Module - client_terminal - client_modulemanager - client_entergame - - game @onLoad: | dofile 'client' diff --git a/modules/client_background/background.lua b/modules/client_background/background.lua index 6e794ae2..cfa8df97 100644 --- a/modules/client_background/background.lua +++ b/modules/client_background/background.lua @@ -11,7 +11,6 @@ function Background.init() local clientVersionLabel = background:getChildById('clientVersionLabel') clientVersionLabel:setText('OTClient ' .. g_app.getVersion() .. '\n' .. 'Rev ' .. g_app.getBuildRevision() .. ' ('.. g_app.getBuildCommit() .. ')\n' .. - 'Protocol ' .. g_game.getProtocolVersion() .. '\n' .. 'Built on ' .. g_app.getBuildDate()) if not g_game.isOnline() then diff --git a/modules/client_entergame/characterlist.lua b/modules/client_entergame/characterlist.lua index b3fdaa94..33ca2fe2 100644 --- a/modules/client_entergame/characterlist.lua +++ b/modules/client_entergame/characterlist.lua @@ -54,7 +54,7 @@ local function updateWait(timeStart, timeEnd) progressBar:setPercent(percent) local label = waitingWindow:getChildById('timeLabel') - label:setText('Trying to reconnect in ' .. timeStr .. ' seconds.') + label:setText(tr('Trying to reconnect in %s seconds.', timeStr)) updateWaitEvent = scheduleEvent(function() updateWait(timeStart, timeEnd) end, 1000 * progressBar:getPercentPixels() / 100 * (timeEnd - timeStart)) return true diff --git a/modules/client_entergame/entergame.lua b/modules/client_entergame/entergame.lua index 64d6773c..5fc0a76e 100644 --- a/modules/client_entergame/entergame.lua +++ b/modules/client_entergame/entergame.lua @@ -5,6 +5,7 @@ local loadBox local enterGame local motdButton local enterGameButton +local protocolBox -- private functions local function onError(protocol, message, errorCode) @@ -66,6 +67,11 @@ function EnterGame.init() local host = g_settings.get('host') local port = g_settings.get('port') local autologin = g_settings.getBoolean('autologin') + local protocol = g_settings.getInteger('protocol', 860) + + if not protocol or protocol == 0 then + protocol = 860 + end if port == nil or port == 0 then port = 7171 end @@ -77,6 +83,12 @@ function EnterGame.init() enterGame:getChildById('rememberPasswordBox'):setChecked(#account > 0) enterGame:getChildById('accountNameTextEdit'):focus() + protocolBox = enterGame:getChildById('protocolComboBox') + for _i,proto in pairs(g_game.getSupportedProtocols()) do + protocolBox:addOption(proto) + end + protocolBox:setCurrentOption(protocol) + -- only open entergame when app starts if not g_app.isRunning() then if #host > 0 and #password > 0 and #account > 0 and autologin then @@ -95,6 +107,7 @@ function EnterGame.terminate() enterGameButton = nil motdButton:destroy() motdButton = nil + protocolBox = nil EnterGame = nil end @@ -116,7 +129,6 @@ function EnterGame.openWindow() end end - function EnterGame.clearAccountFields() enterGame:getChildById('accountNameTextEdit'):clearText() enterGame:getChildById('accountPasswordTextEdit'):clearText() @@ -130,16 +142,18 @@ function EnterGame.doLogin() G.password = enterGame:getChildById('accountPasswordTextEdit'):getText() G.host = enterGame:getChildById('serverHostTextEdit'):getText() G.port = tonumber(enterGame:getChildById('serverPortTextEdit'):getText()) + local protocol = tonumber(protocolBox:getText()) EnterGame.hide() - if G.host == '' or G.port == nil or G.port == 0 then - local errorBox = displayErrorBox(tr('Login Error'), tr('Enter a valid server host and port to login.')) + if g_game.isOnline() then + local errorBox = displayErrorBox(tr('Login Error'), tr('Cannot login while already in game.')) connect(errorBox, { onOk = EnterGame.show }) return end g_settings.set('host', G.host) g_settings.set('port', G.port) + g_settings.set('protocol', protocol) local protocolLogin = ProtocolLogin.create() protocolLogin.onError = onError @@ -153,6 +167,8 @@ function EnterGame.doLogin() EnterGame.show() end }) + g_game.chooseRsa(G.host) + g_game.setProtocolVersion(protocol) protocolLogin:login(G.host, G.port, G.account, G.password) end diff --git a/modules/client_entergame/entergame.otui b/modules/client_entergame/entergame.otui index a204b7a3..4a407453 100644 --- a/modules/client_entergame/entergame.otui +++ b/modules/client_entergame/entergame.otui @@ -1,7 +1,7 @@ MainWindow id: enterGame !text: tr('Enter Game') - size: 236 240 + size: 236 274 @onEnter: EnterGame.doLogin() @onEscape: EnterGame.hide() @@ -43,26 +43,43 @@ MainWindow TextEdit id: serverHostTextEdit !tooltip: tr('Make sure that your client uses\nthe correct game protocol version') - anchors.left: serverLabel.left + anchors.left: parent.left + anchors.right: parent.right anchors.top: serverLabel.bottom margin-top: 2 - width: 140 + + Label + id: protocolLabel + !text: tr('Protocol') + anchors.left: parent.left + anchors.top: serverHostTextEdit.bottom + anchors.right: portLabel.left + margin-right: 10 + margin-top: 8 + + ComboBox + id: protocolComboBox + anchors.left: protocolLabel.left + anchors.right: protocolLabel.right + anchors.top: protocolLabel.bottom + margin-top: 2 + width: 90 Label id: portLabel !text: tr('Port') - anchors.left: serverHostTextEdit.right - anchors.top: serverLabel.top - margin-left: 10 - text-auto-resize: true + anchors.right: parent.right + anchors.top: serverHostTextEdit.bottom + margin-top: 8 + width: 70 TextEdit id: serverPortTextEdit text: 7171 + anchors.right: parent.right anchors.left: portLabel.left anchors.top: portLabel.bottom margin-top: 2 - width: 55 CheckBox id: rememberPasswordBox diff --git a/modules/client_extended/extended.lua b/modules/client_extended/extended.lua index 2a594cad..3bd35641 100644 --- a/modules/client_extended/extended.lua +++ b/modules/client_extended/extended.lua @@ -55,4 +55,4 @@ function Extended.unregister(opcode) callbacks[opcode] = nil return true -end \ No newline at end of file +end diff --git a/modules/corelib/util.lua b/modules/corelib/util.lua index 5242c510..764830d6 100644 --- a/modules/corelib/util.lua +++ b/modules/corelib/util.lua @@ -132,6 +132,7 @@ function getfsrcpath(depth) end function resolvepath(filePath, depth) + if not filePath then return nil end depth = depth or 1 if filePath then if filePath:sub(0, 1) ~= '/' then diff --git a/modules/game/game.otmod b/modules/game/game.otmod deleted file mode 100644 index 3139e17d..00000000 --- a/modules/game/game.otmod +++ /dev/null @@ -1,44 +0,0 @@ -Module - name: game - description: Contains game related classes - author: OTClient team - website: www.otclient.info - - dependencies: - - client_extended - - client_background - - game_tibiafiles - - load-later: - - game_interface - - game_hotkeys - - game_questlog - - game_textmessage - - game_console - - game_outfit - - game_healthinfo - - game_skills - - game_inventory - - game_combatcontrols - - game_containers - - game_viplist - - game_battle - - game_minimap - - game_npctrade - - game_textwindow - - game_playertrade - - game_ruleviolation - - game_bugreport - - game_shaders - - game_playerdeath - - game_playermount - - game_market - - @onLoad: | - dofile 'const' - - dofile 'protocollogin' - - dofile 'creature' - dofile 'player' - dofile 'market' diff --git a/modules/game_battle/battle.lua b/modules/game_battle/battle.lua index 2bcd68d1..a75ec675 100644 --- a/modules/game_battle/battle.lua +++ b/modules/game_battle/battle.lua @@ -224,7 +224,7 @@ function Battle.checkCreatureSkull(creature, skullId) if creature:getSkull() ~= SkullNone then skullWidget:setWidth(skullWidget:getHeight()) local imagePath = getSkullImagePath(creature:getSkull()) - skullWidget:setImageSource('/game/' .. imagePath) + skullWidget:setImageSource(imagePath) labelWidget:setMarginLeft(5) else skullWidget:setWidth(0) @@ -246,7 +246,7 @@ function Battle.checkCreatureEmblem(creature, emblemId) if emblemId ~= EmblemNone then emblemWidget:setWidth(emblemWidget:getHeight()) local imagePath = getEmblemImagePath(emblemId) - emblemWidget:setImageSource('/game/' .. imagePath) + emblemWidget:setImageSource(imagePath) emblemWidget:setMarginLeft(5) labelWidget:setMarginLeft(5) else diff --git a/modules/game_interface/interface.otmod b/modules/game_interface/interface.otmod index 2ad795d7..14726a55 100644 --- a/modules/game_interface/interface.otmod +++ b/modules/game_interface/interface.otmod @@ -4,6 +4,30 @@ Module author: OTClient team website: www.otclient.info + load-later: + - game_hotkeys + - game_questlog + - game_textmessage + - game_console + - game_outfit + - game_healthinfo + - game_skills + - game_inventory + - game_combatcontrols + - game_containers + - game_viplist + - game_battle + - game_minimap + - game_npctrade + - game_textwindow + - game_playertrade + - game_ruleviolation + - game_bugreport + - game_shaders + - game_playerdeath + - game_playermount + - game_market + @onLoad: | dofile 'widgets/uigamemap' dofile 'widgets/uiitem' diff --git a/modules/game_market/market.otmod b/modules/game_market/market.otmod index 2a345723..329f193f 100644 --- a/modules/game_market/market.otmod +++ b/modules/game_market/market.otmod @@ -4,9 +4,6 @@ Module author: BeniS website: www.otclient.info - dependencies: - - game - @onLoad: | dofile 'marketoffer' dofile 'marketprotocol' diff --git a/modules/game_market/marketoffer.lua b/modules/game_market/marketoffer.lua index dbeb2181..13bd16a9 100644 --- a/modules/game_market/marketoffer.lua +++ b/modules/game_market/marketoffer.lua @@ -32,7 +32,7 @@ MarketOffer.new = function(offerId, action, itemId, amount, price, playerName, s offer.player = playerName state = tonumber(state) - if state ~= MarketOfferState.Active and state ~= MarketOfferState.Cancelled + if state ~= MarketOfferState.Active and state ~= MarketOfferState.Cancelled and state ~= MarketOfferState.Expired and state ~= MarketOfferState.Accepted then g_logger.error('MarketOffer.new - invalid state provided.') end diff --git a/modules/game_market/marketprotocol.lua b/modules/game_market/marketprotocol.lua index 207899fe..807d2fa2 100644 --- a/modules/game_market/marketprotocol.lua +++ b/modules/game_market/marketprotocol.lua @@ -3,27 +3,6 @@ MarketProtocol = {} local market -- private functions - -local function parseOpcode(protocol, opcode, msg) - if not g_game.getFeature(GamePlayerMarket) then - return false - end - - -- process msg - if opcode == GameServerOpcodes.GameServerMarketEnter then - parseMarketEnter(msg) - elseif opcode == GameServerOpcodes.GameServerMarketLeave then - parseMarketLeave(msg) - elseif opcode == GameServerOpcodes.GameServerMarketDetail then - parseMarketDetail(msg) - elseif opcode == GameServerOpcodes.GameServerMarketBrowse then - parseMarketBrowse(msg) - else - return false - end - return true -end - local function send(msg) print(msg:getMessageSize()) g_game.getProtocolGame():safeSend(msg) @@ -49,12 +28,11 @@ local function readMarketOffer(msg, action, var) else playerName = msg:getString() end - + return MarketOffer.new({timestamp, counter}, action, itemId, amount, price, playerName, state) end -- parsing protocols - local function parseMarketEnter(msg) local balance = msg:getU32() local offers = msg:getU8() @@ -128,14 +106,18 @@ local function parseMarketBrowse(msg) end -- public functions - function MarketProtocol.init() - connect(ProtocolGame, { onOpcode = parseOpcode } ) - + ProtocolGame.registerOpcode(GameServerOpcodes.GameServerMarketEnter, parseMarketEnter) + ProtocolGame.registerOpcode(GameServerOpcodes.GameServerMarketLeave, parseMarketLeave) + ProtocolGame.registerOpcode(GameServerOpcodes.GameServerMarketDetail, parseMarketDetail) + ProtocolGame.registerOpcode(GameServerOpcodes.GameServerMarketBrowse, parseMarketBrowse) end function MarketProtocol.terminate() - disconnect(ProtocolGame, { onOpcode = parseOpcode } ) + ProtocolGame.unregisterOpcode(GameServerOpcodes.GameServerMarketEnter, parseMarketEnter) + ProtocolGame.unregisterOpcode(GameServerOpcodes.GameServerMarketLeave, parseMarketLeave) + ProtocolGame.unregisterOpcode(GameServerOpcodes.GameServerMarketDetail, parseMarketDetail) + ProtocolGame.unregisterOpcode(GameServerOpcodes.GameServerMarketBrowse, parseMarketBrowse) market = nil MarketProtocol = nil diff --git a/modules/game/const.lua b/modules/gamelib/const.lua similarity index 53% rename from modules/game/const.lua rename to modules/gamelib/const.lua index 2f9fbc45..b67c8fe3 100644 --- a/modules/game/const.lua +++ b/modules/gamelib/const.lua @@ -34,11 +34,6 @@ SouthEast = 5 SouthWest = 6 NorthWest = 7 -LoginServerError = 10 -LoginServerMotd = 20 -LoginServerUpdateNeeded = 30 -LoginServerCharacterList = 100 - GameExtendedOpcode = 0 GameProtocolChecksum = 1 GameAccountNames = 2 @@ -56,7 +51,7 @@ GameChannelPlayerList = 13 GamePlayerMounts = 14 GameEnvironmentEffect = 15 GameCreatureType = 16 -GameCreatureAdditionalInfo = 17 +GameCreatureEmblems = 17 GameCreaturePassableInfo = 18 GameItemAnimationPhase = 19 GameTrucatedPingOpcode = 20 @@ -64,6 +59,25 @@ GameReverseCreatureStack = 21 GameMagicEffectU16 = 22 GamePlayerMarket = 23 -OTSERV_RSA = "109120132967399429278860960508995541528237502902798129123468757937266291492576446330739696001110603907230888610072655818825358503429057592827629436413108566029093628212635953836686562675849720620786279431090218017681061521755056710823876476444260558147179707119674283982419152118103759076030616683978566631413" +OTSERV_RSA = "1091201329673994292788609605089955415282375029027981291234687579" .. + "3726629149257644633073969600111060390723088861007265581882535850" .. + "3429057592827629436413108566029093628212635953836686562675849720" .. + "6207862794310902180176810615217550567108238764764442605581471797" .. + "07119674283982419152118103759076030616683978566631413" + +CIPSOFT_RSA = "1321277432058722840622950990822933849527763264961655079678763618" .. + "4334395343554449668205332383339435179772895415509701210392836078" .. + "6959821132214473291575712138800495033169914814069637740318278150" .. + "2907336840325241747827401343576296990629870233111328210165697754" .. + "88792221429527047321331896351555606801473202394175817" + +OsTypes = { + Linux = 1, + Windows = 2, + Flash = 3, + OtclientLinux = 10, + OtclientWindows = 11, + OtclientMac = 12 +} --- @} \ No newline at end of file +-- @} diff --git a/modules/game/creature.lua b/modules/gamelib/creature.lua similarity index 62% rename from modules/game/creature.lua rename to modules/gamelib/creature.lua index e96508fd..b9b0f5d8 100644 --- a/modules/game/creature.lua +++ b/modules/gamelib/creature.lua @@ -30,73 +30,81 @@ EmblemBlue = 3 -- @} function getSkullImagePath(skullId) + local path if skullId == SkullYellow then - return 'icons/skull_yellow.png' + path = 'icons/skull_yellow.png' elseif skullId == SkullGreen then - return 'icons/skull_green.png' + path = 'icons/skull_green.png' elseif skullId == SkullWhite then - return 'icons/skull_white.png' + path = 'icons/skull_white.png' elseif skullId == SkullRed then - return 'icons/skull_red.png' + path = 'icons/skull_red.png' elseif skullId == SkullBlack then - return 'icons/skull_black.png' + path = 'icons/skull_black.png' elseif skullId == SkullOrange then - return 'icons/skull_orange.png' + path = 'icons/skull_orange.png' end + path = resolvepath(path) + return path end function getShieldImagePathAndBlink(shieldId) + local path if shieldId == ShieldWhiteYellow then - return 'icons/shield_yellow_white.png', false + path = 'icons/shield_yellow_white.png', false elseif shieldId == ShieldWhiteBlue then - return 'icons/shield_blue_white.png', false + path = 'icons/shield_blue_white.png', false elseif shieldId == ShieldBlue then - return 'icons/shield_blue.png', false + path = 'icons/shield_blue.png', false elseif shieldId == ShieldYellow then - return 'icons/shield_yellow.png', false + path = 'icons/shield_yellow.png', false elseif shieldId == ShieldBlueSharedExp then - return 'icons/shield_blue_shared.png', false + path = 'icons/shield_blue_shared.png', false elseif shieldId == ShieldYellowSharedExp then - return 'icons/shield_yellow_shared.png', false + path = 'icons/shield_yellow_shared.png', false elseif shieldId == ShieldBlueNoSharedExpBlink then - return 'icons/shield_blue_not_shared.png', true + path = 'icons/shield_blue_not_shared.png', true elseif shieldId == ShieldYellowNoSharedExpBlink then - return 'icons/shield_yellow_not_shared.png', true + path = 'icons/shield_yellow_not_shared.png', true elseif shieldId == ShieldBlueNoSharedExp then - return 'icons/shield_blue_not_shared.png', false + path = 'icons/shield_blue_not_shared.png', false elseif shieldId == ShieldYellowNoSharedExp then - return 'icons/shield_yellow_not_shared.png', false + path = 'icons/shield_yellow_not_shared.png', false end + path = resolvepath(path) + return path end function getEmblemImagePath(emblemId) + local path if emblemId == EmblemGreen then - return 'icons/emblem_green.png' + path = 'icons/emblem_green.png' elseif emblemId == EmblemRed then - return 'icons/emblem_red.png' + path = 'icons/emblem_red.png' elseif emblemId == EmblemBlue then - return 'icons/emblem_blue.png' + path = 'icons/emblem_blue.png' end + path = resolvepath(path) + return path end function Creature:onSkullChange(skullId) local imagePath = getSkullImagePath(skullId) if imagePath then - self:setSkullTexture(resolvepath(imagePath)) + self:setSkullTexture(imagePath) end end function Creature:onShieldChange(shieldId) local imagePath, blink = getShieldImagePathAndBlink(shieldId) if imagePath then - self:setShieldTexture(resolvepath(imagePath), blink) + self:setShieldTexture(imagePath, blink) end end function Creature:onEmblemChange(emblemId) local imagePath = getEmblemImagePath(emblemId) if imagePath then - self:setEmblemTexture(resolvepath(imagePath)) + self:setEmblemTexture(imagePath) end end - diff --git a/modules/gamelib/game.lua b/modules/gamelib/game.lua new file mode 100644 index 00000000..70531054 --- /dev/null +++ b/modules/gamelib/game.lua @@ -0,0 +1,47 @@ +local currentRsa = OTSERV_RSA + +function g_game.getRsa() + return currentRsa +end + +function g_game.chooseRsa(host) + if host:match('.*\.tibia\.com') or host:match('.*\.cipsoft\.com') then + currentRsa = CIPSOFT_RSA + else + currentRsa = OTSERV_RSA + end +end + +function g_game.setRsa(rsa) + currentRsa = rsa +end + +function g_game.isOfficialTibia() + return currentRsa == CIPSOFT_RSA +end + +function g_game.getOsType() + if g_game.isOfficialTibia() then + if g_app.getOs() == 'windows' then + return OsTypes.Windows + else + return OsTypes.Linux + end + else + if g_app.getOs() == 'windows' then + return OsTypes.OtclientWindows + elseif g_app.getOs() == 'mac' then + return OsTypes.OtclientMac + else + return OsTypes.OtclientLinux + end + end +end + +function g_game.getSupportedProtocols() + return { + 810, 853, 854, 860, 861, 862, 870, 940, + 953, 954, 960 + } +end + diff --git a/modules/gamelib/gamelib.otmod b/modules/gamelib/gamelib.otmod new file mode 100644 index 00000000..167bf823 --- /dev/null +++ b/modules/gamelib/gamelib.otmod @@ -0,0 +1,20 @@ +Module + name: gamelib + description: Contains game related classes + author: OTClient team + website: www.otclient.info + + dependencies: + - client_extended + - game_tibiafiles + + @onLoad: | + dofile 'const' + + dofile 'protocollogin' + dofile 'protocolgame' + dofile 'game' + + dofile 'creature' + dofile 'player' + dofile 'market' diff --git a/modules/game/icons/emblem_blue.png b/modules/gamelib/icons/emblem_blue.png similarity index 100% rename from modules/game/icons/emblem_blue.png rename to modules/gamelib/icons/emblem_blue.png diff --git a/modules/game/icons/emblem_green.png b/modules/gamelib/icons/emblem_green.png similarity index 100% rename from modules/game/icons/emblem_green.png rename to modules/gamelib/icons/emblem_green.png diff --git a/modules/game/icons/emblem_red.png b/modules/gamelib/icons/emblem_red.png similarity index 100% rename from modules/game/icons/emblem_red.png rename to modules/gamelib/icons/emblem_red.png diff --git a/modules/game/icons/shield_blue.png b/modules/gamelib/icons/shield_blue.png similarity index 100% rename from modules/game/icons/shield_blue.png rename to modules/gamelib/icons/shield_blue.png diff --git a/modules/game/icons/shield_blue_not_shared.png b/modules/gamelib/icons/shield_blue_not_shared.png similarity index 100% rename from modules/game/icons/shield_blue_not_shared.png rename to modules/gamelib/icons/shield_blue_not_shared.png diff --git a/modules/game/icons/shield_blue_shared.png b/modules/gamelib/icons/shield_blue_shared.png similarity index 100% rename from modules/game/icons/shield_blue_shared.png rename to modules/gamelib/icons/shield_blue_shared.png diff --git a/modules/game/icons/shield_blue_white.png b/modules/gamelib/icons/shield_blue_white.png similarity index 100% rename from modules/game/icons/shield_blue_white.png rename to modules/gamelib/icons/shield_blue_white.png diff --git a/modules/game/icons/shield_yellow.png b/modules/gamelib/icons/shield_yellow.png similarity index 100% rename from modules/game/icons/shield_yellow.png rename to modules/gamelib/icons/shield_yellow.png diff --git a/modules/game/icons/shield_yellow_not_shared.png b/modules/gamelib/icons/shield_yellow_not_shared.png similarity index 100% rename from modules/game/icons/shield_yellow_not_shared.png rename to modules/gamelib/icons/shield_yellow_not_shared.png diff --git a/modules/game/icons/shield_yellow_shared.png b/modules/gamelib/icons/shield_yellow_shared.png similarity index 100% rename from modules/game/icons/shield_yellow_shared.png rename to modules/gamelib/icons/shield_yellow_shared.png diff --git a/modules/game/icons/shield_yellow_white.png b/modules/gamelib/icons/shield_yellow_white.png similarity index 100% rename from modules/game/icons/shield_yellow_white.png rename to modules/gamelib/icons/shield_yellow_white.png diff --git a/modules/game/icons/skull_black.png b/modules/gamelib/icons/skull_black.png similarity index 100% rename from modules/game/icons/skull_black.png rename to modules/gamelib/icons/skull_black.png diff --git a/modules/game/icons/skull_green.png b/modules/gamelib/icons/skull_green.png similarity index 100% rename from modules/game/icons/skull_green.png rename to modules/gamelib/icons/skull_green.png diff --git a/modules/game/icons/skull_orange.png b/modules/gamelib/icons/skull_orange.png similarity index 100% rename from modules/game/icons/skull_orange.png rename to modules/gamelib/icons/skull_orange.png diff --git a/modules/game/icons/skull_red.png b/modules/gamelib/icons/skull_red.png similarity index 100% rename from modules/game/icons/skull_red.png rename to modules/gamelib/icons/skull_red.png diff --git a/modules/game/icons/skull_white.png b/modules/gamelib/icons/skull_white.png similarity index 100% rename from modules/game/icons/skull_white.png rename to modules/gamelib/icons/skull_white.png diff --git a/modules/game/icons/skull_yellow.png b/modules/gamelib/icons/skull_yellow.png similarity index 100% rename from modules/game/icons/skull_yellow.png rename to modules/gamelib/icons/skull_yellow.png diff --git a/modules/game/market.lua b/modules/gamelib/market.lua similarity index 100% rename from modules/game/market.lua rename to modules/gamelib/market.lua diff --git a/modules/game/player.lua b/modules/gamelib/player.lua similarity index 99% rename from modules/game/player.lua rename to modules/gamelib/player.lua index 9e379574..eee5a9cf 100644 --- a/modules/game/player.lua +++ b/modules/gamelib/player.lua @@ -52,4 +52,3 @@ function Player:hasVip(creatureName) end return false end - diff --git a/modules/gamelib/protocolgame.lua b/modules/gamelib/protocolgame.lua new file mode 100644 index 00000000..ac5950ad --- /dev/null +++ b/modules/gamelib/protocolgame.lua @@ -0,0 +1,23 @@ +local opcodeCallbacks = {} + +function ProtocolGame:onOpcode(opcode, msg) + for i, callback in pairs(opcodeCallbacks) do + if i == opcode then + callback(msg) + return true + end + end + return false +end + +function ProtocolGame.registerOpcode(opcode, callback) + if opcodeCallbacks[opcode] then + error('opcode ' .. opcode .. ' already registered will be overriden') + end + + opcodeCallbacks[opcode] = callback +end + +function ProtocolGame.unregisterOpcode(opcode) + opcodeCallbacks[opcode] = nil +end diff --git a/modules/game/protocollogin.lua b/modules/gamelib/protocollogin.lua similarity index 81% rename from modules/game/protocollogin.lua rename to modules/gamelib/protocollogin.lua index d71b171e..6d3a9792 100644 --- a/modules/game/protocollogin.lua +++ b/modules/gamelib/protocollogin.lua @@ -1,16 +1,25 @@ -- @docclass ProtocolLogin = extends(Protocol) +-- set to the latest Tibia.pic signature to make otclient compatible with official tibia +local PIC_SIGNATURE = 0 + +LoginServerError = 10 +LoginServerMotd = 20 +LoginServerUpdateNeeded = 30 +LoginServerCharacterList = 100 + + -- private functions local function sendLoginPacket(protocol) local msg = OutputMessage.create() msg:addU8(ClientOpcodes.ClientEnterAccount) - msg:addU16(1) -- todo: ClientOs - msg:addU16(g_game.getClientVersion()) + msg:addU16(g_game.getOsType()) + msg:addU16(g_game.getProtocolVersion()) msg:addU32(g_things.getDatSignature()) msg:addU32(g_sprites.getSprSignature()) - msg:addU32(0) -- todo: pic signature + msg:addU32(PIC_SIGNATURE) local paddingBytes = 128 msg:addU8(0) -- first RSA byte must be 0 @@ -40,7 +49,7 @@ local function sendLoginPacket(protocol) end msg:addPaddingBytes(paddingBytes, 0) - msg:encryptRSA(128, OTSERV_RSA) -- todo: check whether to use cip or ot rsa + msg:encryptRsa(128, g_game.getRsa()) protocol:send(msg) protocol:enableXteaEncryption() @@ -60,7 +69,7 @@ function ProtocolLogin:onRecv(msg) elseif opcode == LoginServerMotd then self:parseMotd(msg) elseif opcode == LoginServerUpdateNeeded then - signalcall(self.onError, self, "Client needs update.") + signalcall(self.onError, self, tr("Client needs update.")) elseif opcode == LoginServerCharacterList then self:parseCharacterList(msg) else @@ -77,7 +86,11 @@ end function ProtocolLogin:login(host, port, accountName, accountPassword) if string.len(accountName) == 0 or string.len(accountPassword) == 0 then - signalcall(self.onError, self, "You must enter an account name and password.") + signalcall(self.onError, self, tr("You must enter an account name and password.")) + return + end + if string.len(host) == 0 or port == nil or port == 0 then + signalcall(self.onError, self, tr("You must enter a valid server address and port.")) return end diff --git a/src/framework/core/application.cpp b/src/framework/core/application.cpp index 420f1408..0fdb7576 100644 --- a/src/framework/core/application.cpp +++ b/src/framework/core/application.cpp @@ -141,3 +141,14 @@ void Application::close() if(!g_lua.callGlobalField("g_app", "onClose")) exit(); } + +std::string Application::getOs() +{ +#if defined(WIN32) + return "windows"; +#elif defined(__APPLE__) + return "mac"; +#else + return "linux"; +#endif +} diff --git a/src/framework/core/application.h b/src/framework/core/application.h index c2ecce1b..8cd93174 100644 --- a/src/framework/core/application.h +++ b/src/framework/core/application.h @@ -58,6 +58,7 @@ public: std::string getBuildCommit() { return BUILD_COMMIT; } std::string getBuildType() { return BUILD_TYPE; } std::string getBuildArch() { return BUILD_ARCH; } + std::string getOs(); std::string getStartupOptions() { return m_startupOptions; } protected: diff --git a/src/framework/luafunctions.cpp b/src/framework/luafunctions.cpp index d511d553..375720cd 100644 --- a/src/framework/luafunctions.cpp +++ b/src/framework/luafunctions.cpp @@ -22,22 +22,25 @@ #include #include -#include -#include #include #include #include #include -#include -#include #include #include #include #include #include -#include #include +#ifdef FW_GRAPHICS +#include +#include +#include +#include +#include +#endif + void Application::registerLuaFunctions() { // conversion globals @@ -67,6 +70,7 @@ void Application::registerLuaFunctions() g_lua.bindSingletonFunction("g_app", "getBuildCommit", &Application::getBuildCommit, static_cast(&g_app)); g_lua.bindSingletonFunction("g_app", "getBuildType", &Application::getBuildType, static_cast(&g_app)); g_lua.bindSingletonFunction("g_app", "getBuildArch", &Application::getBuildArch, static_cast(&g_app)); + g_lua.bindSingletonFunction("g_app", "getOs", &Application::getOs, static_cast(&g_app)); g_lua.bindSingletonFunction("g_app", "exit", &Application::exit, static_cast(&g_app)); // Crypt @@ -644,7 +648,7 @@ void Application::registerLuaFunctions() g_lua.bindClassMemberFunction("peekU16", &InputMessage::peekU16); g_lua.bindClassMemberFunction("peekU32", &InputMessage::peekU32); g_lua.bindClassMemberFunction("peekU64", &InputMessage::peekU64); - g_lua.bindClassMemberFunction("decryptRSA", &InputMessage::decryptRSA); + g_lua.bindClassMemberFunction("decryptRsa", &InputMessage::decryptRsa); g_lua.bindClassMemberFunction("getReadSize", &InputMessage::getReadSize); g_lua.bindClassMemberFunction("getUnreadSize", &InputMessage::getUnreadSize); g_lua.bindClassMemberFunction("getMessageSize", &InputMessage::getMessageSize); @@ -661,7 +665,7 @@ void Application::registerLuaFunctions() g_lua.bindClassMemberFunction("addU64", &OutputMessage::addU64); g_lua.bindClassMemberFunction("addString", &OutputMessage::addString); g_lua.bindClassMemberFunction("addPaddingBytes", &OutputMessage::addPaddingBytes); - g_lua.bindClassMemberFunction("encryptRSA", &OutputMessage::encryptRSA); + g_lua.bindClassMemberFunction("encryptRsa", &OutputMessage::encryptRsa); g_lua.bindClassMemberFunction("getMessageSize", &OutputMessage::getMessageSize); #endif diff --git a/src/framework/net/inputmessage.cpp b/src/framework/net/inputmessage.cpp index 807af7a9..80acfc72 100644 --- a/src/framework/net/inputmessage.cpp +++ b/src/framework/net/inputmessage.cpp @@ -86,7 +86,7 @@ std::string InputMessage::getString() return std::string(v, stringLength); } -void InputMessage::decryptRSA(int size, const std::string& p, const std::string& q, const std::string& d) +void InputMessage::decryptRsa(int size, const std::string& p, const std::string& q, const std::string& d) { checkRead(size); RSA::decrypt((char*)m_buffer + m_readPos, size, p.c_str(), q.c_str(), d.c_str()); diff --git a/src/framework/net/inputmessage.h b/src/framework/net/inputmessage.h index 2ed46b0c..12b5a019 100644 --- a/src/framework/net/inputmessage.h +++ b/src/framework/net/inputmessage.h @@ -40,6 +40,7 @@ public: void setBuffer(const std::string& buffer); void skipBytes(uint16 bytes) { m_readPos += bytes; } + void setReadPos(uint16 readPos) { m_readPos = readPos; } uint8 getU8(); uint16 getU16(); uint32 getU32(); @@ -51,9 +52,10 @@ public: uint32 peekU32() { uint32 v = getU32(); m_readPos-=4; return v; } uint64 peekU64() { uint64 v = getU64(); m_readPos-=8; return v; } - void decryptRSA(int size, const std::string& p, const std::string& q, const std::string& d); + void decryptRsa(int size, const std::string& p, const std::string& q, const std::string& d); int getReadSize() { return m_readPos - m_headerPos; } + int getReadPos() { return m_readPos; } int getUnreadSize() { return m_messageSize - (m_readPos - m_headerPos); } uint16 getMessageSize() { return m_messageSize; } diff --git a/src/framework/net/outputmessage.cpp b/src/framework/net/outputmessage.cpp index f8a38425..f3a4725a 100644 --- a/src/framework/net/outputmessage.cpp +++ b/src/framework/net/outputmessage.cpp @@ -89,7 +89,7 @@ void OutputMessage::addPaddingBytes(int bytes, uint8 byte) m_messageSize += bytes; } -void OutputMessage::encryptRSA(int size, const std::string& key) +void OutputMessage::encryptRsa(int size, const std::string& key) { if(m_messageSize < size) throw stdext::exception("insufficient bytes in buffer to encrypt"); diff --git a/src/framework/net/outputmessage.h b/src/framework/net/outputmessage.h index f3ba04aa..4e33f30e 100644 --- a/src/framework/net/outputmessage.h +++ b/src/framework/net/outputmessage.h @@ -49,7 +49,7 @@ public: void addString(const std::string& buffer); void addPaddingBytes(int bytes, uint8 byte = 0); - void encryptRSA(int size, const std::string& key); + void encryptRsa(int size, const std::string& key); uint16 getMessageSize() { return m_messageSize; } diff --git a/src/otclient/const.h b/src/otclient/const.h index bd404567..9c221c82 100644 --- a/src/otclient/const.h +++ b/src/otclient/const.h @@ -286,31 +286,27 @@ namespace Otc }; enum GameFeature { - GameExtendedOpcode = 0, - GameProtocolChecksum, - GameAccountNames, - GameChallangeOnLogin, - GameStackposOnTileAddThing, - GamePenalityOnDeath, - GameNameOnNpcTrade, - GameDoubleFreeCapacity, - GameDoubleExperience, - GameTotalCapacity, - GameSkillsBase, - GameAdditionalPlayerStats, - GameIdOnCancelAttack, - GameChannelPlayerList, - GamePlayerMounts, - GameEnvironmentEffect, - GameCreatureType, - GameCreatureAdditionalInfo, - GameCreaturePassableInfo, - GameItemAnimationPhase, - GameTrucatedPingOpcode, - GameReverseCreatureStack, - GameMagicEffectU16, - GamePlayerMarket, - LastGameFeature + // 1-50 defined in c++ + GameProtocolChecksum = 1, + GameAccountNames = 2, + GameChallangeOnLogin = 3, + GamePenalityOnDeath = 5, + GameNameOnNpcTrade = 6, + GameDoubleFreeCapacity = 7, + GameDoubleExperience = 8, + GameTotalCapacity = 9, + GameSkillsBase = 10, + GamePlayerRegenerationTime = 11, + GameChannelPlayerList = 13, + GamePlayerMounts = 14, + GameEnvironmentEffect = 15, + GameCreatureEmblems = 17, + GameItemAnimationPhase = 19, + GameMagicEffectU16 = 22, + GamePlayerMarket = 23, + // 23-50 unused yet + // 51-100 reserved to be defined in lua + LastGameFeature = 101 }; enum PathFindResult { diff --git a/src/otclient/creature.cpp b/src/otclient/creature.cpp index e6f5c61b..27d63c85 100644 --- a/src/otclient/creature.cpp +++ b/src/otclient/creature.cpp @@ -461,6 +461,8 @@ void Creature::setDirection(Otc::Direction direction) void Creature::setOutfit(const Outfit& outfit) { + if(!g_things.isValidDatId(outfit.getId(), DatCreatureCategory)) + return; m_walkAnimationPhase = 0; // might happen when player is walking and outfit is changed. m_outfit = outfit; } diff --git a/src/otclient/effect.cpp b/src/otclient/effect.cpp index 529df785..4a487215 100644 --- a/src/otclient/effect.cpp +++ b/src/otclient/effect.cpp @@ -46,6 +46,8 @@ void Effect::startAnimation() void Effect::setId(uint32 id) { + if(!g_things.isValidDatId(id, DatEffectCategory)) + id = 0; m_id = id; } diff --git a/src/otclient/game.cpp b/src/otclient/game.cpp index 323df744..e2e2acc9 100644 --- a/src/otclient/game.cpp +++ b/src/otclient/game.cpp @@ -38,7 +38,7 @@ Game g_game; Game::Game() { resetGameStates(); - setClientVersion(860); + m_protocolVersion = 0; } void Game::resetGameStates() @@ -416,10 +416,11 @@ void Game::processWalkCancel(Otc::Direction direction) 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) { - if(m_protocolGame || isOnline()) { - g_logger.traceError("unable to login into a world while already online or logging"); - return; - } + if(m_protocolGame || isOnline()) + stdext::throw_exception("Unable to login into a world while already online or logging."); + + if(m_protocolVersion == 0) + stdext::throw_exception("Must set a valid game protocol version before logging."); m_protocolGame = ProtocolGamePtr(new ProtocolGame); m_protocolGame->login(account, password, worldHost, (uint16)worldPort, characterName); @@ -1090,61 +1091,48 @@ bool Game::canPerformGameAction() return m_localPlayer && !m_dead && m_protocolGame && m_protocolGame->isConnected() && checkBotProtection(); } -void Game::setClientVersion(int clientVersion) +void Game::setProtocolVersion(int version) { - if(isOnline()) { - g_logger.error("Unable to change client version while online"); - return; - } + if(isOnline()) + stdext::throw_exception("Unable to change client version while online"); - //TODO: check supported versions + if(version < 810 || version > 960) + stdext::throw_exception(stdext::format("Protocol version %d not supported", version)); m_features.reset(); - if(clientVersion >= 854) { + if(version >= 854) { enableFeature(Otc::GameProtocolChecksum); enableFeature(Otc::GameAccountNames); enableFeature(Otc::GameChallangeOnLogin); - enableFeature(Otc::GameStackposOnTileAddThing); enableFeature(Otc::GameDoubleFreeCapacity); - enableFeature(Otc::GameCreatureAdditionalInfo); - enableFeature(Otc::GameReverseCreatureStack); - } - - if(clientVersion >= 860) { - enableFeature(Otc::GameIdOnCancelAttack); + enableFeature(Otc::GameCreatureEmblems); } - if(clientVersion >= 862) { + if(version >= 862) { enableFeature(Otc::GamePenalityOnDeath); } - if(clientVersion >= 870) { + if(version >= 870) { enableFeature(Otc::GameDoubleExperience); enableFeature(Otc::GamePlayerMounts); } - if(clientVersion >= 910) { + if(version >= 910) { enableFeature(Otc::GameNameOnNpcTrade); enableFeature(Otc::GameTotalCapacity); enableFeature(Otc::GameSkillsBase); - enableFeature(Otc::GameAdditionalPlayerStats); + enableFeature(Otc::GamePlayerRegenerationTime); enableFeature(Otc::GameChannelPlayerList); enableFeature(Otc::GameEnvironmentEffect); - enableFeature(Otc::GameCreatureType); enableFeature(Otc::GameItemAnimationPhase); } - if(clientVersion >= 940) { + if(version >= 940) { enableFeature(Otc::GamePlayerMarket); } - if(clientVersion >= 953) { - enableFeature(Otc::GameCreaturePassableInfo); - enableFeature(Otc::GameTrucatedPingOpcode); - } - - m_clientVersion = clientVersion; + m_protocolVersion = version; } void Game::setAttackingCreature(const CreaturePtr& creature) diff --git a/src/otclient/game.h b/src/otclient/game.h index 3f00c8df..b70a5a70 100644 --- a/src/otclient/game.h +++ b/src/otclient/game.h @@ -232,8 +232,8 @@ public: void setFeature(Otc::GameFeature feature) { m_features.set(feature, false); } bool getFeature(Otc::GameFeature feature) { return m_features.test(feature); } - void setClientVersion(int clientVersion); - int getClientVersion() { return m_clientVersion; } + void setProtocolVersion(int version); + int getProtocolVersion() { return m_protocolVersion; } void setRSA(const std::string& rsa); std::string getRSA() { return m_rsa; } @@ -256,7 +256,6 @@ public: int getServerBeat() { return m_serverBeat; } LocalPlayerPtr getLocalPlayer() { return m_localPlayer; } ProtocolGamePtr getProtocolGame() { return m_protocolGame; } - int getProtocolVersion() { return PROTOCOL; } std::string getCharacterName() { return m_characterName; } std::string getWorldName() { return m_worldName; } std::vector getGMActions() { return m_gmActions; } @@ -288,7 +287,7 @@ private: std::string m_characterName; std::string m_worldName; std::bitset m_features; - int m_clientVersion; + int m_protocolVersion; std::string m_rsa; }; diff --git a/src/otclient/item.cpp b/src/otclient/item.cpp index 65dc375e..39e32851 100644 --- a/src/otclient/item.cpp +++ b/src/otclient/item.cpp @@ -101,7 +101,7 @@ void Item::draw(const Point& dest, float scaleFactor, bool animate) else if(tile->mustHookEast()) xPattern = getNumPatternX() >= 3 ? 2 : 0; } - } else if(isFluid() || isFluidContainer()) { + } else if(isSplash() || isFluidContainer()) { int color = Otc::FluidTransparent; switch(m_countOrSubType) { case Otc::FluidNone: @@ -176,15 +176,20 @@ void Item::draw(const Point& dest, float scaleFactor, bool animate) void Item::setId(uint32 id) { - m_otbId = g_things.findOtbForClientId(id)->getServerId(); + if(!g_things.isValidDatId(id, DatItemCategory)) + id = 0; + //m_otbId = g_things.findOtbForClientId(id)->getServerId(); m_id = id; + m_otbId = 0; } void Item::setOtbId(uint16 id) { + if(!g_things.isValidOtbId(id)) + id = 0; auto otbType = g_things.getOtbType(id); - m_otbId = id; m_id = otbType->getClientId(); + m_otbId = id; } bool Item::isValid() diff --git a/src/otclient/luafunctions.cpp b/src/otclient/luafunctions.cpp index 287bc3a3..d9c420f2 100644 --- a/src/otclient/luafunctions.cpp +++ b/src/otclient/luafunctions.cpp @@ -190,10 +190,10 @@ void OTClient::registerLuaFunctions() g_lua.bindSingletonFunction("g_game", "getLocalPlayer", &Game::getLocalPlayer, &g_game); g_lua.bindSingletonFunction("g_game", "getProtocolGame", &Game::getProtocolGame, &g_game); g_lua.bindSingletonFunction("g_game", "getProtocolVersion", &Game::getProtocolVersion, &g_game); + g_lua.bindSingletonFunction("g_game", "setProtocolVersion", &Game::setProtocolVersion, &g_game); g_lua.bindSingletonFunction("g_game", "getCharacterName", &Game::getCharacterName, &g_game); g_lua.bindSingletonFunction("g_game", "getWorldName", &Game::getWorldName, &g_game); g_lua.bindSingletonFunction("g_game", "getGMActions", &Game::getGMActions, &g_game); - g_lua.bindSingletonFunction("g_game", "getClientVersion", &Game::getClientVersion, &g_game); g_lua.bindSingletonFunction("g_game", "getFeature", &Game::getFeature, &g_game); g_lua.registerSingletonClass("g_shaders"); diff --git a/src/otclient/map.cpp b/src/otclient/map.cpp index 5ca3bd56..71760a8f 100644 --- a/src/otclient/map.cpp +++ b/src/otclient/map.cpp @@ -467,7 +467,7 @@ bool Map::loadOtcm(const std::string& fileName) uint8 countOrSubType = fin->getU8(); ItemPtr item = Item::create(id); - if(item->isStackable() || item->isFluidContainer() || item->isFluid()) + if(item->isStackable() || item->isFluidContainer() || item->isSplash() || item->isChargeable()) item->setCountOrSubType(countOrSubType); if(item->isValid()) diff --git a/src/otclient/missile.cpp b/src/otclient/missile.cpp index dcd6db2e..eb7f03cd 100644 --- a/src/otclient/missile.cpp +++ b/src/otclient/missile.cpp @@ -83,6 +83,8 @@ void Missile::setPath(const Position& fromPosition, const Position& toPosition) void Missile::setId(uint32 id) { + if(!g_things.isValidDatId(id, DatMissileCategory)) + id = 0; m_id = id; } diff --git a/src/otclient/outfit.cpp b/src/otclient/outfit.cpp index 549b4892..527c282d 100644 --- a/src/otclient/outfit.cpp +++ b/src/otclient/outfit.cpp @@ -25,7 +25,7 @@ Outfit::Outfit() { m_category = DatCreatureCategory; - m_id = 1; + m_id = 0; resetClothes(); } diff --git a/src/otclient/protocolcodes.h b/src/otclient/protocolcodes.h index b30c274d..51b64962 100644 --- a/src/otclient/protocolcodes.h +++ b/src/otclient/protocolcodes.h @@ -25,49 +25,7 @@ #include "global.h" -#if !(PROTOCOL == 810) && \ - !(PROTOCOL == 854) && \ - !(PROTOCOL >= 860 && PROTOCOL <= 862) && \ - !(PROTOCOL >= 870 && PROTOCOL <= 871) && \ - !(PROTOCOL >= 910 && PROTOCOL <= 953) -#error "the supplied protocol version is not supported" -#endif - namespace Proto { -#ifdef CIPSOFT_RSA - constexpr const char* RSA = "1321277432058722840622950990822933849527763264961655079678763618" - "4334395343554449668205332383339435179772895415509701210392836078" - "6959821132214473291575712138800495033169914814069637740318278150" - "2907336840325241747827401343576296990629870233111328210165697754" - "88792221429527047321331896351555606801473202394175817"; -#else - constexpr const char* RSA = "1091201329673994292788609605089955415282375029027981291234687579" - "3726629149257644633073969600111060390723088861007265581882535850" - "3429057592827629436413108566029093628212635953836686562675849720" - "6207862794310902180176810615217550567108238764764442605581471797" - "07119674283982419152118103759076030616683978566631413"; -#endif - - constexpr int PicSignature = 0x4F8C231A; // 953 pic signature - constexpr int ClientVersion = PROTOCOL; - - enum OsTypes { - OsLinux = 1, - OsWindows = 2, - OsFlash = 3, - OsOtclientLinux = 10, - OsOtclientWindows = 11, - OsOtclientMac = 12 - }; - -#ifdef OSTYPE - constexpr int ClientOs = OSTYPE; -#elif defined WIN32 - constexpr int ClientOs = OsOtclientWindows; -#else - constexpr int ClientOs = OsOtclientLinux; -#endif - enum LoginServerOpts { LoginServerError = 10, LoginServerMotd = 20, diff --git a/src/otclient/protocolgameparse.cpp b/src/otclient/protocolgameparse.cpp index 2d1c2002..b1f1635a 100644 --- a/src/otclient/protocolgameparse.cpp +++ b/src/otclient/protocolgameparse.cpp @@ -43,8 +43,11 @@ void ProtocolGame::parseMessage(const InputMessagePtr& msg) opcode = msg->getU8(); // try to parse in lua first + int readPos = msg->getReadPos(); if(callLuaField("onOpcode", opcode, msg)) continue; + else + msg->setReadPos(readPos); // restore read pos if(!m_gameInitialized && opcode > Proto::GameServerFirstGameOpcode) 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) parseLoginWait(msg); break; case Proto::GameServerPing: - if(g_game.getFeature(Otc::GameTrucatedPingOpcode)) + case Proto::GameServerPingBack: + if((opcode == Proto::GameServerPing && g_game.getProtocolVersion() >= 953) || + (opcode == Proto::GameServerPingBack && g_game.getProtocolVersion() < 953)) parsePingBack(msg); else parsePing(msg); break; - case Proto::GameServerPingBack: - if(g_game.getFeature(Otc::GameTrucatedPingOpcode)) - parsePing(msg); - else - parsePingBack(msg); - break; case Proto::GameServerChallange: parseChallange(msg); break; @@ -334,9 +333,9 @@ void ProtocolGame::parseGMActions(const InputMessagePtr& msg) int numViolationReasons; - if(g_game.getClientVersion() >= 860) + if(g_game.getProtocolVersion() >= 860) numViolationReasons = 20; - else if(g_game.getClientVersion() >= 854) + else if(g_game.getProtocolVersion() >= 854) numViolationReasons = 19; else numViolationReasons = 32; @@ -454,7 +453,7 @@ void ProtocolGame::parseTileAddThing(const InputMessagePtr& msg) Position pos = getPosition(msg); int stackPos = -1; - if(g_game.getFeature(Otc::GameStackposOnTileAddThing)) + if(g_game.getProtocolVersion() >= 854) stackPos = msg->getU8(); ThingPtr thing = getThing(msg); @@ -508,7 +507,7 @@ void ProtocolGame::parseCreatureMove(const InputMessagePtr& msg) int stackPos = -2; // older protocols stores creatures in reverse order - if(!g_game.getFeature(Otc::GameReverseCreatureStack)) + if(!g_game.getProtocolVersion() >= 854) stackPos = -1; g_map.addThing(thing, newPos, stackPos); @@ -884,12 +883,11 @@ void ProtocolGame::parsePlayerStats(const InputMessagePtr& msg) m_localPlayer->setStamina(stamina); m_localPlayer->setSoul(soul); - if(g_game.getFeature(Otc::GameAdditionalPlayerStats)) { - int speed = msg->getU16(); - msg->getU16(); // regeneration time + if(g_game.getProtocolVersion() >= 910) + m_localPlayer->setSpeed(msg->getU16()); - m_localPlayer->setSpeed(speed); - } + if(g_game.getFeature(Otc::GamePlayerRegenerationTime)) + msg->getU16(); // regeneration time } void ProtocolGame::parsePlayerSkills(const InputMessagePtr& msg) @@ -913,7 +911,7 @@ void ProtocolGame::parsePlayerState(const InputMessagePtr& msg) void ProtocolGame::parsePlayerCancelAttack(const InputMessagePtr& msg) { - if(g_game.getFeature(Otc::GameIdOnCancelAttack)) + if(g_game.getProtocolVersion() >= 860) msg->getU32(); // unknown g_game.processAttackCancel(); @@ -1389,7 +1387,7 @@ CreaturePtr ProtocolGame::getCreature(const InputMessagePtr& msg, int type) uint id = msg->getU32(); int creatureType; - if(g_game.getFeature(Otc::GameCreatureType)) + if(g_game.getProtocolVersion() >= 910) creatureType = msg->getU8(); else { if(id >= Proto::PlayerStartId && id < Proto::PlayerEndId) @@ -1441,12 +1439,11 @@ CreaturePtr ProtocolGame::getCreature(const InputMessagePtr& msg, int type) int emblem = -1; bool passable = false; - if(g_game.getFeature(Otc::GameCreatureAdditionalInfo)) { - if(!known) - emblem = msg->getU8(); + if(g_game.getFeature(Otc::GameCreatureEmblems) && !known) + emblem = msg->getU8(); + if(g_game.getProtocolVersion() >= 854) passable = (msg->getU8() == 0); - } if(creature) { creature->setHealthPercent(healthPercent); @@ -1474,7 +1471,7 @@ CreaturePtr ProtocolGame::getCreature(const InputMessagePtr& msg, int type) if(creature) creature->turn(direction); - if(g_game.getFeature(Otc::GameCreaturePassableInfo)) { + if(g_game.getProtocolVersion() >= 953) { bool passable = msg->getU8(); if(creature) @@ -1497,7 +1494,7 @@ ItemPtr ProtocolGame::getItem(const InputMessagePtr& msg, int id) if(item->getId() == 0) stdext::throw_exception("unable to create item with invalid id 0"); - if(item->isStackable() || item->isFluidContainer() || item->isFluid()) + if(item->isStackable() || item->isFluidContainer() || item->isSplash() || item->isChargeable()) item->setCountOrSubType(msg->getU8()); if(g_game.getFeature(Otc::GameItemAnimationPhase)) { diff --git a/src/otclient/protocolgamesend.cpp b/src/otclient/protocolgamesend.cpp index 45beebeb..4ca3d3a2 100644 --- a/src/otclient/protocolgamesend.cpp +++ b/src/otclient/protocolgamesend.cpp @@ -49,8 +49,9 @@ void ProtocolGame::sendLoginPacket(uint challangeTimestamp, uint8 challangeRando OutputMessagePtr msg(new OutputMessage); msg->addU8(Proto::ClientEnterGame); - msg->addU16(Proto::ClientOs); - msg->addU16(g_game.getClientVersion()); + + msg->addU16(g_lua.callGlobalField("g_game", "getOs")); + msg->addU16(g_game.getProtocolVersion()); int paddingBytes = 128; msg->addU8(0); // first RSA byte must be 0 @@ -90,7 +91,7 @@ void ProtocolGame::sendLoginPacket(uint challangeTimestamp, uint8 challangeRando msg->addPaddingBytes(paddingBytes); // encrypt with RSA - msg->encryptRSA(128, Proto::RSA); + msg->encryptRsa(128, g_lua.callGlobalField("g_game", "getRsa")); send(msg); @@ -570,9 +571,10 @@ void ProtocolGame::sendShareExperience(bool active, int unknown) OutputMessagePtr msg(new OutputMessage); msg->addU8(Proto::ClientShareExperience); msg->addU8(active ? 0x01 : 0x00); -#if PROTOCOL<910 - msg->addU8(unknown); -#endif + + if(g_game.getProtocolVersion() < 910) + msg->addU8(unknown); + send(msg); } diff --git a/src/otclient/thing.h b/src/otclient/thing.h index ef606e73..4fb1dcc7 100644 --- a/src/otclient/thing.h +++ b/src/otclient/thing.h @@ -28,12 +28,6 @@ #include "thingtypemanager.h" #include -struct Light -{ - uint8 intensity; - uint8 color; -}; - // @bindclass #pragma pack(push,1) // disable memory alignment class Thing : public LuaObject @@ -95,8 +89,7 @@ public: int getAnimationPhases() { return rawGetDatType()->getAnimationPhases(); } int getGroundSpeed() { return rawGetDatType()->getGroundSpeed(); } int getMaxTextLength() { return rawGetDatType()->getMaxTextLength(); } - int getLightLevel() { return rawGetDatType()->getLightLevel(); } - int getLightColor() { return rawGetDatType()->getLightColor(); } + Light getLight() { return rawGetDatType()->getLight(); } int getMinimapColor() { return rawGetDatType()->getMinimapColor(); } int getLensHelp() { return rawGetDatType()->getLensHelp(); } int getClothSlot() { return rawGetDatType()->getClothSlot(); } @@ -110,9 +103,10 @@ public: bool isForceUse() { return rawGetDatType()->isForceUse(); } bool isMultiUse() { return rawGetDatType()->isMultiUse(); } bool isWritable() { return rawGetDatType()->isWritable(); } + bool isChargeable() { return rawGetDatType()->isChargeable(); } bool isWritableOnce() { return rawGetDatType()->isWritableOnce(); } bool isFluidContainer() { return rawGetDatType()->isFluidContainer(); } - bool isFluid() { return rawGetDatType()->isFluid(); } + bool isSplash() { return rawGetDatType()->isSplash(); } bool isNotWalkable() { return rawGetDatType()->isNotWalkable(); } bool isNotMoveable() { return rawGetDatType()->isNotMoveable(); } bool blockProjectile() { return rawGetDatType()->blockProjectile(); } @@ -134,6 +128,7 @@ public: bool isFullGround() { return rawGetDatType()->isFullGround(); } bool isIgnoreLook() { return rawGetDatType()->isIgnoreLook(); } bool isCloth() { return rawGetDatType()->isCloth(); } + MarketData getMarketData() { return rawGetDatType()->getMarketData(); } protected: Position m_position; diff --git a/src/otclient/thingtypedat.cpp b/src/otclient/thingtypedat.cpp index 8d0b78fa..c0522030 100644 --- a/src/otclient/thingtypedat.cpp +++ b/src/otclient/thingtypedat.cpp @@ -22,6 +22,7 @@ #include "thingtypedat.h" #include "spritemanager.h" +#include "game.h" #include #include @@ -33,20 +34,11 @@ ThingTypeDat::ThingTypeDat() { m_category = DatInvalidCategory; m_id = 0; + m_null = true; m_exactSize = 0; - m_layers = 0; - m_numPatternX = 0; - m_numPatternY = 0; - m_numPatternZ = 0; + m_numPatternX = m_numPatternY = m_numPatternZ = 0; m_animationPhases = 0; - m_groundSpeed = 0; - m_maxTextLenght = 0; - m_lightLevel = 0; - m_lightColor = 0; - m_miniMapColor = 0; - m_lensHelp = 0; - m_clothSlot = 0; - m_elevation = 0; + m_layers = 0; } void ThingTypeDat::unserialize(uint16 clientId, DatCategory category, const FileStreamPtr& fin) @@ -55,136 +47,68 @@ void ThingTypeDat::unserialize(uint16 clientId, DatCategory category, const File m_id = clientId; m_category = category; + + static int datVersion; + if(clientId == 100 && category == DatItemCategory) + datVersion = 2; + bool done = false; for(int i = 0 ; i < DatLastAttrib;++i) { - int property = fin->getU8(); - if(property == DatLastAttrib) { + int attrib = fin->getU8(); + if(attrib == DatLastAttrib) { done = true; break; } - switch(property) { - case DatAttribIsGround: - m_isGround = true; - m_groundSpeed = fin->getU16(); - if(m_groundSpeed == 0) - m_groundSpeed = 100; - break; - case DatAttribIsGroundBorder: - m_isGroundBorder = true; - break; - case DatAttribIsOnBottom: - m_isOnBottom = true; - break; - case DatAttribIsOnTop: - m_isOnTop = true; - break; - case DatAttribIsContainer: - m_isContainer = true; - break; - case DatAttribIsStackable: - m_isStackable = true; - break; - case DatAttribIsForceUse: - m_isForceUse = true; - break; - case DatAttribIsMultiUse: - m_isMultiUse = true; - break; - case DatAttribIsWritable: - m_isWritable = true; - m_maxTextLenght = fin->getU16(); - break; - case DatAttribIsWritableOnce: - m_isWritableOnce = true; - m_maxTextLenght = fin->getU16(); - break; - case DatAttribIsFluidContainer: - m_isFluidContainer = true; - break; - case DatAttribIsFluid: - m_isFluid = true; - break; - case DatAttribIsNotWalkable: - m_isNotWalkable = true; - break; - case DatAttribIsNotMoveable: - m_isNotMoveable = true; - break; - case DatAttribBlockProjectile: - m_blockProjectile = true; - break; - case DatAttribIsNotPathable: - m_isNotPathable = true; - break; - case DatAttribIsPickupable: - m_isPickupable = true; - break; - case DatAttribIsHangable: - m_isHangable = true; - break; - case DatAttribHookSouth: - m_isHookSouth = true; - break; - case DatAttribHookEast: - m_isHookEast = true; - break; - case DatAttribIsRotateable: - m_isRotateable = true; - break; - case DatAttribHasLight: - m_hasLight = true; - m_lightLevel = fin->getU16(); - m_lightColor = fin->getU16(); - break; - case DatAttribDontHide: - m_isDontHide = true; - break; - case DatAttribIsTranslucent: - m_isTranslucent = true; - break; - case DatAttribHasDisplacement: - m_hasDisplacement = true; - m_displacement = Point(fin->getU16(), fin->getU16()); - break; - case DatAttribHasElevation: - m_hasElevation = true; - m_elevation = fin->getU16(); + // hacky way to detect if is older dat version or not + if(clientId == 100 && category == DatItemCategory && datVersion != 1 && + (attrib == DatAttribNotPathable || attrib == DatAttribDontHide || attrib == DatAttribIgnoreLook)) { + datVersion = 1; + } + if(datVersion <= 1) { + if(attrib == DatAttribWritable) { + m_attribs.set(DatAttribChargeable, true); + continue; + } else if(attrib > DatAttribWritable) + attrib -= 1; + } + + switch(attrib) { + case DatAttribDisplacement: { + m_displacement.x = fin->getU16(); + m_displacement.y = fin->getU16(); + m_attribs.set(attrib, true); break; - case DatAttribIsLyingCorpse: - m_isLyingCorpse = true; + } + case DatAttribLight: { + Light light; + light.intensity = fin->getU16(); + light.color = fin->getU16(); + m_attribs.set(attrib, light); break; - case DatAttribAnimateAlways: - m_isAnimateAlways = true; + } + case DatAttribMarket: { + MarketData market; + market.category = fin->getU16(); + market.showAs = fin->getU16(); + market.tradeAs = fin->getU16(); + market.name = fin->getString(); + market.restrictProfession = fin->getU16(); + market.requiredLevel = fin->getU16(); + m_attribs.set(attrib, market); break; + } + case DatAttribGround: + case DatAttribWritable: + case DatAttribWritableOnce: + case DatAttribElevation: case DatAttribMiniMapColor: - m_miniMapColor = true; - m_miniMapColor = fin->getU16(); - break; - case DatAttribLensHelp: - m_lensHelp = true; - m_lensHelp = fin->getU16(); - break; - case DatAttribIsFullGround: - m_isFullGround = true; - break; - case DatAttribIgnoreLook: - m_isIgnoreLook = true; - break; case DatAttribCloth: - m_isCloth = true; - m_clothSlot = fin->getU16(); - break; - case DatAttribMarket: - fin->getU16(); // category - fin->getU16(); // trade as - fin->getU16(); // show as - fin->getString(); // name - fin->getU16(); // restrict profession - fin->getU16(); // level + case DatAttribLensHelp: + m_attribs.set(attrib, fin->getU16()); break; default: - stdext::throw_exception("corrupt data, invalid type attribute"); + m_attribs.set(attrib, true); break; }; } @@ -192,40 +116,18 @@ void ThingTypeDat::unserialize(uint16 clientId, DatCategory category, const File if(!done) stdext::throw_exception("corrupt data"); - int totalSprites = 1; - for(int i = 0; i < DatLastDimension; ++i) { - switch(i) { - case DatWidth: - m_size.setWidth(fin->getU8()); - break; - case DatHeight: - m_size.setHeight(fin->getU8()); - break; - case DatExactSize: - if(m_size.width() <= 1 && m_size.height() <= 1) - m_exactSize = 32; - else - m_exactSize = std::min((int)fin->getU8(), std::max(m_size.width() * 32, m_size.height() * 32)); - break; - case DatLayers: - m_layers = fin->getU8(); - break; - case DatPatternX: - m_numPatternX = fin->getU8(); - break; - case DatPatternY: - m_numPatternY = fin->getU8(); - break; - case DatPatternZ: - m_numPatternZ = fin->getU8(); - break; - case DatAnimationPhases: - m_animationPhases = fin->getU8(); - break; - } - } + uint8 width = fin->getU8(); + uint8 height = fin->getU8(); + m_size = Size(width, height); + m_exactSize = (width > 1 || height > 1) ? std::min((int)fin->getU8(), std::max(width * 32, height * 32)) : 32; + m_layers = fin->getU8(); + m_numPatternX = fin->getU8(); + m_numPatternY = fin->getU8(); + m_numPatternZ = fin->getU8(); + m_animationPhases = fin->getU8(); + + int totalSprites = m_size.area() * m_layers * m_numPatternX * m_numPatternY * m_numPatternZ * m_animationPhases; - totalSprites = m_size.width() * m_size.height() * m_layers * m_numPatternX * m_numPatternY * m_numPatternZ * m_animationPhases; if(totalSprites == 0) stdext::throw_exception("a thing type has no sprites"); if(totalSprites > 4096) diff --git a/src/otclient/thingtypedat.h b/src/otclient/thingtypedat.h index 1d5e7f27..6ea268a9 100644 --- a/src/otclient/thingtypedat.h +++ b/src/otclient/thingtypedat.h @@ -29,6 +29,7 @@ #include #include #include +#include enum DatCategory { DatItemCategory = 0, @@ -48,53 +49,59 @@ enum DatSpriteMask { }; enum DatAttrib { - DatAttribIsGround = 0, - DatAttribIsGroundBorder, - DatAttribIsOnBottom, - DatAttribIsOnTop, - DatAttribIsContainer, - DatAttribIsStackable, - DatAttribIsForceUse, - DatAttribIsMultiUse, - DatAttribIsWritable, - DatAttribIsWritableOnce, - DatAttribIsFluidContainer, - DatAttribIsFluid, - DatAttribIsNotWalkable, - DatAttribIsNotMoveable, + DatAttribGround = 0, + DatAttribGroundBorder, + DatAttribOnBottom, + DatAttribOnTop, + DatAttribContainer, + DatAttribStackable, + DatAttribForceUse, + DatAttribMultiUse, + //DatAttribRune + DatAttribWritable, + DatAttribWritableOnce, + DatAttribFluidContainer, + DatAttribSplash, + DatAttribNotWalkable, + DatAttribNotMoveable, DatAttribBlockProjectile, - DatAttribIsNotPathable, - DatAttribIsPickupable, - DatAttribIsHangable, + DatAttribNotPathable, + DatAttribPickupable, + DatAttribHangable, DatAttribHookSouth, DatAttribHookEast, - DatAttribIsRotateable, - DatAttribHasLight, + DatAttribRotateable, + DatAttribLight, DatAttribDontHide, - DatAttribIsTranslucent, - DatAttribHasDisplacement, - DatAttribHasElevation, - DatAttribIsLyingCorpse, + DatAttribTranslucent, + DatAttribDisplacement, + DatAttribElevation, + DatAttribLyingCorpse, DatAttribAnimateAlways, DatAttribMiniMapColor, DatAttribLensHelp, - DatAttribIsFullGround, + DatAttribFullGround, DatAttribIgnoreLook, DatAttribCloth, DatAttribMarket, - DatLastAttrib = 255 + DatLastAttrib = 255, + + // legacy attribs + DatAttribChargeable = 254 +}; + +struct MarketData { + std::string name; + int category; + uint16 requiredLevel; + uint16 restrictProfession; + uint16 showAs; + uint16 tradeAs; }; -enum DatDimension { - DatWidth = 0, - DatHeight, - DatExactSize, - DatLayers, - DatPatternX, - DatPatternY, - DatPatternZ, - DatAnimationPhases, - DatLastDimension +struct Light { + uint8 intensity; + uint8 color; }; class ThingTypeDat : public LuaObject @@ -113,56 +120,58 @@ public: Size getSize() { return m_size; } int getWidth() { return m_size.width(); } int getHeight() { return m_size.height(); } - Point getDisplacement() { return m_displacement; } - int getDisplacementX() { return m_displacement.x; } - int getDisplacementY() { return m_displacement.y; } int getExactSize() { return m_exactSize; } int getLayers() { return m_layers; } int getNumPatternX() { return m_numPatternX; } int getNumPatternY() { return m_numPatternY; } int getNumPatternZ() { return m_numPatternZ; } int getAnimationPhases() { return m_animationPhases; } - int getGroundSpeed() { return m_groundSpeed; } - int getMaxTextLength() { return m_maxTextLenght; } - int getLightLevel() { return m_lightLevel; } - int getLightColor() { return m_lightColor; } - int getMinimapColor() { return m_miniMapColor; } - int getLensHelp() { return m_lensHelp; } - int getClothSlot() { return m_clothSlot; } - int getElevation() { return m_elevation; } - bool isGround() { return m_isGround; } - bool isGroundBorder() { return m_isGroundBorder; } - bool isOnBottom() { return m_isOnBottom; } - bool isOnTop() { return m_isOnTop; } - bool isContainer() { return m_isContainer; } - bool isStackable() { return m_isStackable; } - bool isForceUse() { return m_isForceUse; } - bool isMultiUse() { return m_isMultiUse; } - bool isWritable() { return m_isWritable; } - bool isWritableOnce() { return m_isWritableOnce; } - bool isFluidContainer() { return m_isFluidContainer; } - bool isFluid() { return m_isFluid; } - bool isNotWalkable() { return m_isNotWalkable; } - bool isNotMoveable() { return m_isNotMoveable; } - bool blockProjectile() { return m_blockProjectile; } - bool isNotPathable() { return m_isNotPathable; } - bool isPickupable() { return m_isPickupable; } - bool isHangable() { return m_isHangable; } - bool isHookSouth() { return m_isHookSouth; } - bool isHookEast() { return m_isHookEast; } - bool isRotateable() { return m_isRotateable; } - bool hasLight() { return m_hasLight; } - bool isDontHide() { return m_isDontHide; } - bool isTranslucent() { return m_isTranslucent; } - bool hasDisplacement() { return m_hasDisplacement; } - bool hasElevation() { return m_hasElevation; } - bool isLyingCorpse() { return m_isLyingCorpse; } - bool isAnimateAlways() { return m_isAnimateAlways; } - bool hasMiniMapColor() { return m_hasMiniMapColor; } - bool hasLensHelp() { return m_hasLensHelp; } - bool isFullGround() { return m_isFullGround; } - bool isIgnoreLook() { return m_isIgnoreLook; } - bool isCloth() { return m_isCloth; } + Point getDisplacement() { return m_displacement; } + int getDisplacementX() { return getDisplacement().x; } + int getDisplacementY() { return getDisplacement().y; } + + int getGroundSpeed() { return m_attribs.get(DatAttribGround); } + int getMaxTextLength() { return m_attribs.has(DatAttribWritableOnce) ? m_attribs.get(DatAttribWritableOnce) : m_attribs.get(DatAttribWritable); } + Light getLight() { return m_attribs.get(DatAttribLight); } + int getMinimapColor() { return m_attribs.get(DatAttribMiniMapColor); } + int getLensHelp() { return m_attribs.get(DatAttribLensHelp); } + int getClothSlot() { return m_attribs.get(DatAttribCloth); } + int getElevation() { return m_attribs.get(DatAttribElevation); } + MarketData getMarketData() { return m_attribs.get(DatAttribMarket); } + bool isGround() { return m_attribs.has(DatAttribGround); } + bool isGroundBorder() { return m_attribs.has(DatAttribGroundBorder); } + bool isOnBottom() { return m_attribs.has(DatAttribOnBottom); } + bool isOnTop() { return m_attribs.has(DatAttribOnTop); } + bool isContainer() { return m_attribs.has(DatAttribContainer); } + bool isStackable() { return m_attribs.has(DatAttribStackable); } + bool isForceUse() { return m_attribs.has(DatAttribForceUse); } + bool isMultiUse() { return m_attribs.has(DatAttribMultiUse); } + bool isWritable() { return m_attribs.has(DatAttribWritable); } + bool isChargeable() { return m_attribs.has(DatAttribChargeable); } + bool isWritableOnce() { return m_attribs.has(DatAttribWritableOnce); } + bool isFluidContainer() { return m_attribs.has(DatAttribFluidContainer); } + bool isSplash() { return m_attribs.has(DatAttribSplash); } + bool isNotWalkable() { return m_attribs.has(DatAttribNotWalkable); } + bool isNotMoveable() { return m_attribs.has(DatAttribNotMoveable); } + bool blockProjectile() { return m_attribs.has(DatAttribBlockProjectile); } + bool isNotPathable() { return m_attribs.has(DatAttribNotPathable); } + bool isPickupable() { return m_attribs.has(DatAttribPickupable); } + bool isHangable() { return m_attribs.has(DatAttribHangable); } + bool isHookSouth() { return m_attribs.has(DatAttribHookSouth); } + bool isHookEast() { return m_attribs.has(DatAttribHookEast); } + bool isRotateable() { return m_attribs.has(DatAttribRotateable); } + bool hasLight() { return m_attribs.has(DatAttribLight); } + bool isDontHide() { return m_attribs.has(DatAttribDontHide); } + bool isTranslucent() { return m_attribs.has(DatAttribTranslucent); } + bool hasDisplacement() { return m_attribs.has(DatAttribDisplacement); } + bool hasElevation() { return m_attribs.has(DatAttribElevation); } + bool isLyingCorpse() { return m_attribs.has(DatAttribLyingCorpse); } + bool isAnimateAlways() { return m_attribs.has(DatAttribAnimateAlways); } + bool hasMiniMapColor() { return m_attribs.has(DatAttribMiniMapColor); } + bool hasLensHelp() { return m_attribs.has(DatAttribLensHelp); } + bool isFullGround() { return m_attribs.has(DatAttribFullGround); } + bool isIgnoreLook() { return m_attribs.has(DatAttribIgnoreLook); } + bool isCloth() { return m_attribs.has(DatAttribCloth); } private: const TexturePtr& getTexture(int animationPhase); @@ -172,64 +181,21 @@ private: DatCategory m_category; uint16 m_id; - Boolean m_null; + bool m_null; + AttribStorage m_attribs; + + Size m_size; + Point m_displacement; + int m_exactSize; + int m_numPatternX, m_numPatternY, m_numPatternZ; + int m_animationPhases; + int m_layers; std::vector m_spritesIndex; std::vector m_textures; std::vector> m_texturesFramesRects; std::vector> m_texturesFramesOriginRects; std::vector> m_texturesFramesOffsets; - - // dat stuff - Size m_size; - Point m_displacement; - int m_exactSize; - int m_layers; - int m_numPatternX; - int m_numPatternY; - int m_numPatternZ; - int m_animationPhases; - int m_groundSpeed; - int m_maxTextLenght; - int m_lightLevel; - int m_lightColor; - int m_miniMapColor; - int m_lensHelp; - int m_clothSlot; - int m_elevation; - Boolean m_isGround; - Boolean m_isGroundBorder; - Boolean m_isOnBottom; - Boolean m_isOnTop; - Boolean m_isContainer; - Boolean m_isStackable; - Boolean m_isForceUse; - Boolean m_isMultiUse; - Boolean m_isWritable; - Boolean m_isWritableOnce; - Boolean m_isFluidContainer; - Boolean m_isFluid; - Boolean m_isNotWalkable; - Boolean m_isNotMoveable; - Boolean m_blockProjectile; - Boolean m_isNotPathable; - Boolean m_isPickupable; - Boolean m_isHangable; - Boolean m_isHookSouth; - Boolean m_isHookEast; - Boolean m_isRotateable; - Boolean m_hasLight; - Boolean m_isDontHide; - Boolean m_isTranslucent; - Boolean m_hasDisplacement; - Boolean m_hasElevation; - Boolean m_isLyingCorpse; - Boolean m_isAnimateAlways; - Boolean m_hasMiniMapColor; - Boolean m_hasLensHelp; - Boolean m_isFullGround; - Boolean m_isIgnoreLook; - Boolean m_isCloth; }; #endif diff --git a/src/otclient/tile.cpp b/src/otclient/tile.cpp index 46147813..e42f6ff9 100644 --- a/src/otclient/tile.cpp +++ b/src/otclient/tile.cpp @@ -367,7 +367,7 @@ ThingPtr Tile::getTopMultiUseThing() for(uint i = 0; i < m_things.size(); ++i) { ThingPtr thing = m_things[i]; if(thing->isForceUse() || (!thing->isGround() && !thing->isGroundBorder() && !thing->isOnBottom() && !thing->isOnTop())) { - if(i > 0 && thing->isFluid()) + if(i > 0 && thing->isSplash()) return m_things[i-1]; return thing; } diff --git a/src/otclient/uiitem.cpp b/src/otclient/uiitem.cpp index ecdf837c..8bffc38a 100644 --- a/src/otclient/uiitem.cpp +++ b/src/otclient/uiitem.cpp @@ -58,7 +58,7 @@ void UIItem::drawSelf(Fw::DrawPane drawPane) g_painter->setColor(Color::white); m_item->draw(dest, scaleFactor, true); - if(m_font && m_item->isStackable() && m_item->getCount() > 1) { + if(m_font && (m_item->isStackable() || m_item->isChargeable()) && m_item->getCount() > 1) { std::string count = stdext::to_string(m_item->getCount()); g_painter->setColor(Color(231, 231, 231)); m_font->drawText(count, Rect(m_rect.topLeft(), m_rect.bottomRight() - Point(3, 0)), Fw::AlignBottomRight);