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
This commit is contained in:
Eduardo Bart 2012-07-17 20:49:21 -03:00
parent 6fc11d2fa9
commit eb24d6776e
64 changed files with 536 additions and 588 deletions

View File

@ -29,17 +29,18 @@ g_configs.load("/config.otml")
g_modules.discoverModules() g_modules.discoverModules()
-- core modules 0-99 -- libraries modules 0-99
g_modules.autoLoadModules(99) g_modules.autoLoadModules(99);
g_modules.ensureModuleLoaded("corelib") g_modules.ensureModuleLoaded("corelib")
g_modules.ensureModuleLoaded("gamelib")
-- client modules 100-499 -- client modules 100-499
g_modules.autoLoadModules(499) g_modules.autoLoadModules(499)
g_modules.ensureModuleLoaded("client") g_modules.ensureModuleLoaded("client")
-- game modules 500-999 -- game modules 500-999
g_modules.autoLoadModules(999) g_modules.autoLoadModules(999);
g_modules.ensureModuleLoaded("game") g_modules.ensureModuleLoaded("game_interface")
-- mods 1000-9999 -- mods 1000-9999
g_modules.autoLoadModules(9999) g_modules.autoLoadModules(9999)

View File

@ -16,7 +16,6 @@ Module
- client_terminal - client_terminal
- client_modulemanager - client_modulemanager
- client_entergame - client_entergame
- game
@onLoad: | @onLoad: |
dofile 'client' dofile 'client'

View File

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

View File

@ -54,7 +54,7 @@ local function updateWait(timeStart, timeEnd)
progressBar:setPercent(percent) progressBar:setPercent(percent)
local label = waitingWindow:getChildById('timeLabel') 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)) updateWaitEvent = scheduleEvent(function() updateWait(timeStart, timeEnd) end, 1000 * progressBar:getPercentPixels() / 100 * (timeEnd - timeStart))
return true return true

View File

@ -5,6 +5,7 @@ local loadBox
local enterGame local enterGame
local motdButton local motdButton
local enterGameButton local enterGameButton
local protocolBox
-- private functions -- private functions
local function onError(protocol, message, errorCode) local function onError(protocol, message, errorCode)
@ -66,6 +67,11 @@ function EnterGame.init()
local host = g_settings.get('host') local host = g_settings.get('host')
local port = g_settings.get('port') local port = g_settings.get('port')
local autologin = g_settings.getBoolean('autologin') 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 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('rememberPasswordBox'):setChecked(#account > 0)
enterGame:getChildById('accountNameTextEdit'):focus() 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 -- only open entergame when app starts
if not g_app.isRunning() then if not g_app.isRunning() then
if #host > 0 and #password > 0 and #account > 0 and autologin then if #host > 0 and #password > 0 and #account > 0 and autologin then
@ -95,6 +107,7 @@ function EnterGame.terminate()
enterGameButton = nil enterGameButton = nil
motdButton:destroy() motdButton:destroy()
motdButton = nil motdButton = nil
protocolBox = nil
EnterGame = nil EnterGame = nil
end end
@ -116,7 +129,6 @@ function EnterGame.openWindow()
end end
end end
function EnterGame.clearAccountFields() function EnterGame.clearAccountFields()
enterGame:getChildById('accountNameTextEdit'):clearText() enterGame:getChildById('accountNameTextEdit'):clearText()
enterGame:getChildById('accountPasswordTextEdit'):clearText() enterGame:getChildById('accountPasswordTextEdit'):clearText()
@ -130,16 +142,18 @@ function EnterGame.doLogin()
G.password = enterGame:getChildById('accountPasswordTextEdit'):getText() G.password = enterGame:getChildById('accountPasswordTextEdit'):getText()
G.host = enterGame:getChildById('serverHostTextEdit'):getText() G.host = enterGame:getChildById('serverHostTextEdit'):getText()
G.port = tonumber(enterGame:getChildById('serverPortTextEdit'):getText()) G.port = tonumber(enterGame:getChildById('serverPortTextEdit'):getText())
local protocol = tonumber(protocolBox:getText())
EnterGame.hide() EnterGame.hide()
if G.host == '' or G.port == nil or G.port == 0 then if g_game.isOnline() then
local errorBox = displayErrorBox(tr('Login Error'), tr('Enter a valid server host and port to login.')) local errorBox = displayErrorBox(tr('Login Error'), tr('Cannot login while already in game.'))
connect(errorBox, { onOk = EnterGame.show }) connect(errorBox, { onOk = EnterGame.show })
return return
end end
g_settings.set('host', G.host) g_settings.set('host', G.host)
g_settings.set('port', G.port) g_settings.set('port', G.port)
g_settings.set('protocol', protocol)
local protocolLogin = ProtocolLogin.create() local protocolLogin = ProtocolLogin.create()
protocolLogin.onError = onError protocolLogin.onError = onError
@ -153,6 +167,8 @@ function EnterGame.doLogin()
EnterGame.show() EnterGame.show()
end }) end })
g_game.chooseRsa(G.host)
g_game.setProtocolVersion(protocol)
protocolLogin:login(G.host, G.port, G.account, G.password) protocolLogin:login(G.host, G.port, G.account, G.password)
end end

View File

@ -1,7 +1,7 @@
MainWindow MainWindow
id: enterGame id: enterGame
!text: tr('Enter Game') !text: tr('Enter Game')
size: 236 240 size: 236 274
@onEnter: EnterGame.doLogin() @onEnter: EnterGame.doLogin()
@onEscape: EnterGame.hide() @onEscape: EnterGame.hide()
@ -43,26 +43,43 @@ MainWindow
TextEdit TextEdit
id: serverHostTextEdit id: serverHostTextEdit
!tooltip: tr('Make sure that your client uses\nthe correct game protocol version') !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 anchors.top: serverLabel.bottom
margin-top: 2 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 Label
id: portLabel id: portLabel
!text: tr('Port') !text: tr('Port')
anchors.left: serverHostTextEdit.right anchors.right: parent.right
anchors.top: serverLabel.top anchors.top: serverHostTextEdit.bottom
margin-left: 10 margin-top: 8
text-auto-resize: true width: 70
TextEdit TextEdit
id: serverPortTextEdit id: serverPortTextEdit
text: 7171 text: 7171
anchors.right: parent.right
anchors.left: portLabel.left anchors.left: portLabel.left
anchors.top: portLabel.bottom anchors.top: portLabel.bottom
margin-top: 2 margin-top: 2
width: 55
CheckBox CheckBox
id: rememberPasswordBox id: rememberPasswordBox

View File

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

View File

@ -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'

View File

@ -224,7 +224,7 @@ function Battle.checkCreatureSkull(creature, skullId)
if creature:getSkull() ~= SkullNone then if creature:getSkull() ~= SkullNone then
skullWidget:setWidth(skullWidget:getHeight()) skullWidget:setWidth(skullWidget:getHeight())
local imagePath = getSkullImagePath(creature:getSkull()) local imagePath = getSkullImagePath(creature:getSkull())
skullWidget:setImageSource('/game/' .. imagePath) skullWidget:setImageSource(imagePath)
labelWidget:setMarginLeft(5) labelWidget:setMarginLeft(5)
else else
skullWidget:setWidth(0) skullWidget:setWidth(0)
@ -246,7 +246,7 @@ function Battle.checkCreatureEmblem(creature, emblemId)
if emblemId ~= EmblemNone then if emblemId ~= EmblemNone then
emblemWidget:setWidth(emblemWidget:getHeight()) emblemWidget:setWidth(emblemWidget:getHeight())
local imagePath = getEmblemImagePath(emblemId) local imagePath = getEmblemImagePath(emblemId)
emblemWidget:setImageSource('/game/' .. imagePath) emblemWidget:setImageSource(imagePath)
emblemWidget:setMarginLeft(5) emblemWidget:setMarginLeft(5)
labelWidget:setMarginLeft(5) labelWidget:setMarginLeft(5)
else else

View File

@ -4,6 +4,30 @@ Module
author: OTClient team author: OTClient team
website: www.otclient.info 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: | @onLoad: |
dofile 'widgets/uigamemap' dofile 'widgets/uigamemap'
dofile 'widgets/uiitem' dofile 'widgets/uiitem'

View File

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

View File

@ -3,27 +3,6 @@ MarketProtocol = {}
local market local market
-- private functions -- 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) local function send(msg)
print(msg:getMessageSize()) print(msg:getMessageSize())
g_game.getProtocolGame():safeSend(msg) g_game.getProtocolGame():safeSend(msg)
@ -54,7 +33,6 @@ local function readMarketOffer(msg, action, var)
end end
-- parsing protocols -- parsing protocols
local function parseMarketEnter(msg) local function parseMarketEnter(msg)
local balance = msg:getU32() local balance = msg:getU32()
local offers = msg:getU8() local offers = msg:getU8()
@ -128,14 +106,18 @@ local function parseMarketBrowse(msg)
end end
-- public functions -- public functions
function MarketProtocol.init() 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 end
function MarketProtocol.terminate() 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 market = nil
MarketProtocol = nil MarketProtocol = nil

View File

@ -34,11 +34,6 @@ SouthEast = 5
SouthWest = 6 SouthWest = 6
NorthWest = 7 NorthWest = 7
LoginServerError = 10
LoginServerMotd = 20
LoginServerUpdateNeeded = 30
LoginServerCharacterList = 100
GameExtendedOpcode = 0 GameExtendedOpcode = 0
GameProtocolChecksum = 1 GameProtocolChecksum = 1
GameAccountNames = 2 GameAccountNames = 2
@ -56,7 +51,7 @@ GameChannelPlayerList = 13
GamePlayerMounts = 14 GamePlayerMounts = 14
GameEnvironmentEffect = 15 GameEnvironmentEffect = 15
GameCreatureType = 16 GameCreatureType = 16
GameCreatureAdditionalInfo = 17 GameCreatureEmblems = 17
GameCreaturePassableInfo = 18 GameCreaturePassableInfo = 18
GameItemAnimationPhase = 19 GameItemAnimationPhase = 19
GameTrucatedPingOpcode = 20 GameTrucatedPingOpcode = 20
@ -64,6 +59,25 @@ GameReverseCreatureStack = 21
GameMagicEffectU16 = 22 GameMagicEffectU16 = 22
GamePlayerMarket = 23 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
}
-- @} -- @}

View File

@ -30,73 +30,81 @@ EmblemBlue = 3
-- @} -- @}
function getSkullImagePath(skullId) function getSkullImagePath(skullId)
local path
if skullId == SkullYellow then if skullId == SkullYellow then
return 'icons/skull_yellow.png' path = 'icons/skull_yellow.png'
elseif skullId == SkullGreen then elseif skullId == SkullGreen then
return 'icons/skull_green.png' path = 'icons/skull_green.png'
elseif skullId == SkullWhite then elseif skullId == SkullWhite then
return 'icons/skull_white.png' path = 'icons/skull_white.png'
elseif skullId == SkullRed then elseif skullId == SkullRed then
return 'icons/skull_red.png' path = 'icons/skull_red.png'
elseif skullId == SkullBlack then elseif skullId == SkullBlack then
return 'icons/skull_black.png' path = 'icons/skull_black.png'
elseif skullId == SkullOrange then elseif skullId == SkullOrange then
return 'icons/skull_orange.png' path = 'icons/skull_orange.png'
end end
path = resolvepath(path)
return path
end end
function getShieldImagePathAndBlink(shieldId) function getShieldImagePathAndBlink(shieldId)
local path
if shieldId == ShieldWhiteYellow then if shieldId == ShieldWhiteYellow then
return 'icons/shield_yellow_white.png', false path = 'icons/shield_yellow_white.png', false
elseif shieldId == ShieldWhiteBlue then elseif shieldId == ShieldWhiteBlue then
return 'icons/shield_blue_white.png', false path = 'icons/shield_blue_white.png', false
elseif shieldId == ShieldBlue then elseif shieldId == ShieldBlue then
return 'icons/shield_blue.png', false path = 'icons/shield_blue.png', false
elseif shieldId == ShieldYellow then elseif shieldId == ShieldYellow then
return 'icons/shield_yellow.png', false path = 'icons/shield_yellow.png', false
elseif shieldId == ShieldBlueSharedExp then elseif shieldId == ShieldBlueSharedExp then
return 'icons/shield_blue_shared.png', false path = 'icons/shield_blue_shared.png', false
elseif shieldId == ShieldYellowSharedExp then elseif shieldId == ShieldYellowSharedExp then
return 'icons/shield_yellow_shared.png', false path = 'icons/shield_yellow_shared.png', false
elseif shieldId == ShieldBlueNoSharedExpBlink then elseif shieldId == ShieldBlueNoSharedExpBlink then
return 'icons/shield_blue_not_shared.png', true path = 'icons/shield_blue_not_shared.png', true
elseif shieldId == ShieldYellowNoSharedExpBlink then elseif shieldId == ShieldYellowNoSharedExpBlink then
return 'icons/shield_yellow_not_shared.png', true path = 'icons/shield_yellow_not_shared.png', true
elseif shieldId == ShieldBlueNoSharedExp then elseif shieldId == ShieldBlueNoSharedExp then
return 'icons/shield_blue_not_shared.png', false path = 'icons/shield_blue_not_shared.png', false
elseif shieldId == ShieldYellowNoSharedExp then elseif shieldId == ShieldYellowNoSharedExp then
return 'icons/shield_yellow_not_shared.png', false path = 'icons/shield_yellow_not_shared.png', false
end end
path = resolvepath(path)
return path
end end
function getEmblemImagePath(emblemId) function getEmblemImagePath(emblemId)
local path
if emblemId == EmblemGreen then if emblemId == EmblemGreen then
return 'icons/emblem_green.png' path = 'icons/emblem_green.png'
elseif emblemId == EmblemRed then elseif emblemId == EmblemRed then
return 'icons/emblem_red.png' path = 'icons/emblem_red.png'
elseif emblemId == EmblemBlue then elseif emblemId == EmblemBlue then
return 'icons/emblem_blue.png' path = 'icons/emblem_blue.png'
end end
path = resolvepath(path)
return path
end end
function Creature:onSkullChange(skullId) function Creature:onSkullChange(skullId)
local imagePath = getSkullImagePath(skullId) local imagePath = getSkullImagePath(skullId)
if imagePath then if imagePath then
self:setSkullTexture(resolvepath(imagePath)) self:setSkullTexture(imagePath)
end end
end end
function Creature:onShieldChange(shieldId) function Creature:onShieldChange(shieldId)
local imagePath, blink = getShieldImagePathAndBlink(shieldId) local imagePath, blink = getShieldImagePathAndBlink(shieldId)
if imagePath then if imagePath then
self:setShieldTexture(resolvepath(imagePath), blink) self:setShieldTexture(imagePath, blink)
end end
end end
function Creature:onEmblemChange(emblemId) function Creature:onEmblemChange(emblemId)
local imagePath = getEmblemImagePath(emblemId) local imagePath = getEmblemImagePath(emblemId)
if imagePath then if imagePath then
self:setEmblemTexture(resolvepath(imagePath)) self:setEmblemTexture(imagePath)
end end
end end

47
modules/gamelib/game.lua Normal file
View File

@ -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

View File

@ -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'

View File

Before

Width:  |  Height:  |  Size: 385 B

After

Width:  |  Height:  |  Size: 385 B

View File

Before

Width:  |  Height:  |  Size: 381 B

After

Width:  |  Height:  |  Size: 381 B

View File

Before

Width:  |  Height:  |  Size: 386 B

After

Width:  |  Height:  |  Size: 386 B

View File

Before

Width:  |  Height:  |  Size: 352 B

After

Width:  |  Height:  |  Size: 352 B

View File

Before

Width:  |  Height:  |  Size: 522 B

After

Width:  |  Height:  |  Size: 522 B

View File

Before

Width:  |  Height:  |  Size: 516 B

After

Width:  |  Height:  |  Size: 516 B

View File

Before

Width:  |  Height:  |  Size: 404 B

After

Width:  |  Height:  |  Size: 404 B

View File

Before

Width:  |  Height:  |  Size: 377 B

After

Width:  |  Height:  |  Size: 377 B

View File

Before

Width:  |  Height:  |  Size: 512 B

After

Width:  |  Height:  |  Size: 512 B

View File

Before

Width:  |  Height:  |  Size: 494 B

After

Width:  |  Height:  |  Size: 494 B

View File

Before

Width:  |  Height:  |  Size: 407 B

After

Width:  |  Height:  |  Size: 407 B

View File

Before

Width:  |  Height:  |  Size: 482 B

After

Width:  |  Height:  |  Size: 482 B

View File

Before

Width:  |  Height:  |  Size: 438 B

After

Width:  |  Height:  |  Size: 438 B

View File

Before

Width:  |  Height:  |  Size: 445 B

After

Width:  |  Height:  |  Size: 445 B

View File

Before

Width:  |  Height:  |  Size: 421 B

After

Width:  |  Height:  |  Size: 421 B

View File

Before

Width:  |  Height:  |  Size: 437 B

After

Width:  |  Height:  |  Size: 437 B

View File

Before

Width:  |  Height:  |  Size: 437 B

After

Width:  |  Height:  |  Size: 437 B

View File

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

View File

@ -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

View File

@ -1,16 +1,25 @@
-- @docclass -- @docclass
ProtocolLogin = extends(Protocol) 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 -- private functions
local function sendLoginPacket(protocol) local function sendLoginPacket(protocol)
local msg = OutputMessage.create() local msg = OutputMessage.create()
msg:addU8(ClientOpcodes.ClientEnterAccount) msg:addU8(ClientOpcodes.ClientEnterAccount)
msg:addU16(1) -- todo: ClientOs msg:addU16(g_game.getOsType())
msg:addU16(g_game.getClientVersion()) msg:addU16(g_game.getProtocolVersion())
msg:addU32(g_things.getDatSignature()) msg:addU32(g_things.getDatSignature())
msg:addU32(g_sprites.getSprSignature()) msg:addU32(g_sprites.getSprSignature())
msg:addU32(0) -- todo: pic signature msg:addU32(PIC_SIGNATURE)
local paddingBytes = 128 local paddingBytes = 128
msg:addU8(0) -- first RSA byte must be 0 msg:addU8(0) -- first RSA byte must be 0
@ -40,7 +49,7 @@ local function sendLoginPacket(protocol)
end end
msg:addPaddingBytes(paddingBytes, 0) 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:send(msg)
protocol:enableXteaEncryption() protocol:enableXteaEncryption()
@ -60,7 +69,7 @@ function ProtocolLogin:onRecv(msg)
elseif opcode == LoginServerMotd then elseif opcode == LoginServerMotd then
self:parseMotd(msg) self:parseMotd(msg)
elseif opcode == LoginServerUpdateNeeded then elseif opcode == LoginServerUpdateNeeded then
signalcall(self.onError, self, "Client needs update.") signalcall(self.onError, self, tr("Client needs update."))
elseif opcode == LoginServerCharacterList then elseif opcode == LoginServerCharacterList then
self:parseCharacterList(msg) self:parseCharacterList(msg)
else else
@ -77,7 +86,11 @@ end
function ProtocolLogin:login(host, port, accountName, accountPassword) function ProtocolLogin:login(host, port, accountName, accountPassword)
if string.len(accountName) == 0 or string.len(accountPassword) == 0 then 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 return
end end

View File

@ -141,3 +141,14 @@ void Application::close()
if(!g_lua.callGlobalField<bool>("g_app", "onClose")) if(!g_lua.callGlobalField<bool>("g_app", "onClose"))
exit(); exit();
} }
std::string Application::getOs()
{
#if defined(WIN32)
return "windows";
#elif defined(__APPLE__)
return "mac";
#else
return "linux";
#endif
}

View File

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

View File

@ -22,22 +22,25 @@
#include <framework/core/application.h> #include <framework/core/application.h>
#include <framework/luaengine/luainterface.h> #include <framework/luaengine/luainterface.h>
#include <framework/graphics/fontmanager.h>
#include <framework/ui/ui.h>
#include <framework/net/protocol.h> #include <framework/net/protocol.h>
#include <framework/core/eventdispatcher.h> #include <framework/core/eventdispatcher.h>
#include <framework/core/configmanager.h> #include <framework/core/configmanager.h>
#include <framework/otml/otml.h> #include <framework/otml/otml.h>
#include <framework/graphics/graphics.h>
#include <framework/platform/platformwindow.h>
#include <framework/core/modulemanager.h> #include <framework/core/modulemanager.h>
#include <framework/core/module.h> #include <framework/core/module.h>
#include <framework/sound/soundmanager.h> #include <framework/sound/soundmanager.h>
#include <framework/util/crypt.h> #include <framework/util/crypt.h>
#include <framework/core/resourcemanager.h> #include <framework/core/resourcemanager.h>
#include <framework/graphics/particlemanager.h>
#include <framework/graphics/texturemanager.h> #include <framework/graphics/texturemanager.h>
#ifdef FW_GRAPHICS
#include <framework/graphics/graphics.h>
#include <framework/platform/platformwindow.h>
#include <framework/graphics/particlemanager.h>
#include <framework/graphics/fontmanager.h>
#include <framework/ui/ui.h>
#endif
void Application::registerLuaFunctions() void Application::registerLuaFunctions()
{ {
// conversion globals // conversion globals
@ -67,6 +70,7 @@ void Application::registerLuaFunctions()
g_lua.bindSingletonFunction("g_app", "getBuildCommit", &Application::getBuildCommit, static_cast<Application*>(&g_app)); g_lua.bindSingletonFunction("g_app", "getBuildCommit", &Application::getBuildCommit, static_cast<Application*>(&g_app));
g_lua.bindSingletonFunction("g_app", "getBuildType", &Application::getBuildType, static_cast<Application*>(&g_app)); g_lua.bindSingletonFunction("g_app", "getBuildType", &Application::getBuildType, static_cast<Application*>(&g_app));
g_lua.bindSingletonFunction("g_app", "getBuildArch", &Application::getBuildArch, static_cast<Application*>(&g_app)); g_lua.bindSingletonFunction("g_app", "getBuildArch", &Application::getBuildArch, static_cast<Application*>(&g_app));
g_lua.bindSingletonFunction("g_app", "getOs", &Application::getOs, static_cast<Application*>(&g_app));
g_lua.bindSingletonFunction("g_app", "exit", &Application::exit, static_cast<Application*>(&g_app)); g_lua.bindSingletonFunction("g_app", "exit", &Application::exit, static_cast<Application*>(&g_app));
// Crypt // Crypt
@ -644,7 +648,7 @@ void Application::registerLuaFunctions()
g_lua.bindClassMemberFunction<InputMessage>("peekU16", &InputMessage::peekU16); g_lua.bindClassMemberFunction<InputMessage>("peekU16", &InputMessage::peekU16);
g_lua.bindClassMemberFunction<InputMessage>("peekU32", &InputMessage::peekU32); g_lua.bindClassMemberFunction<InputMessage>("peekU32", &InputMessage::peekU32);
g_lua.bindClassMemberFunction<InputMessage>("peekU64", &InputMessage::peekU64); g_lua.bindClassMemberFunction<InputMessage>("peekU64", &InputMessage::peekU64);
g_lua.bindClassMemberFunction<InputMessage>("decryptRSA", &InputMessage::decryptRSA); g_lua.bindClassMemberFunction<InputMessage>("decryptRsa", &InputMessage::decryptRsa);
g_lua.bindClassMemberFunction<InputMessage>("getReadSize", &InputMessage::getReadSize); g_lua.bindClassMemberFunction<InputMessage>("getReadSize", &InputMessage::getReadSize);
g_lua.bindClassMemberFunction<InputMessage>("getUnreadSize", &InputMessage::getUnreadSize); g_lua.bindClassMemberFunction<InputMessage>("getUnreadSize", &InputMessage::getUnreadSize);
g_lua.bindClassMemberFunction<InputMessage>("getMessageSize", &InputMessage::getMessageSize); g_lua.bindClassMemberFunction<InputMessage>("getMessageSize", &InputMessage::getMessageSize);
@ -661,7 +665,7 @@ void Application::registerLuaFunctions()
g_lua.bindClassMemberFunction<OutputMessage>("addU64", &OutputMessage::addU64); g_lua.bindClassMemberFunction<OutputMessage>("addU64", &OutputMessage::addU64);
g_lua.bindClassMemberFunction<OutputMessage>("addString", &OutputMessage::addString); g_lua.bindClassMemberFunction<OutputMessage>("addString", &OutputMessage::addString);
g_lua.bindClassMemberFunction<OutputMessage>("addPaddingBytes", &OutputMessage::addPaddingBytes); g_lua.bindClassMemberFunction<OutputMessage>("addPaddingBytes", &OutputMessage::addPaddingBytes);
g_lua.bindClassMemberFunction<OutputMessage>("encryptRSA", &OutputMessage::encryptRSA); g_lua.bindClassMemberFunction<OutputMessage>("encryptRsa", &OutputMessage::encryptRsa);
g_lua.bindClassMemberFunction<OutputMessage>("getMessageSize", &OutputMessage::getMessageSize); g_lua.bindClassMemberFunction<OutputMessage>("getMessageSize", &OutputMessage::getMessageSize);
#endif #endif

View File

@ -86,7 +86,7 @@ std::string InputMessage::getString()
return std::string(v, stringLength); 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); checkRead(size);
RSA::decrypt((char*)m_buffer + m_readPos, size, p.c_str(), q.c_str(), d.c_str()); RSA::decrypt((char*)m_buffer + m_readPos, size, p.c_str(), q.c_str(), d.c_str());

View File

@ -40,6 +40,7 @@ public:
void setBuffer(const std::string& buffer); void setBuffer(const std::string& buffer);
void skipBytes(uint16 bytes) { m_readPos += bytes; } void skipBytes(uint16 bytes) { m_readPos += bytes; }
void setReadPos(uint16 readPos) { m_readPos = readPos; }
uint8 getU8(); uint8 getU8();
uint16 getU16(); uint16 getU16();
uint32 getU32(); uint32 getU32();
@ -51,9 +52,10 @@ public:
uint32 peekU32() { uint32 v = getU32(); m_readPos-=4; return v; } uint32 peekU32() { uint32 v = getU32(); m_readPos-=4; return v; }
uint64 peekU64() { uint64 v = getU64(); m_readPos-=8; 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 getReadSize() { return m_readPos - m_headerPos; }
int getReadPos() { return m_readPos; }
int getUnreadSize() { return m_messageSize - (m_readPos - m_headerPos); } int getUnreadSize() { return m_messageSize - (m_readPos - m_headerPos); }
uint16 getMessageSize() { return m_messageSize; } uint16 getMessageSize() { return m_messageSize; }

View File

@ -89,7 +89,7 @@ void OutputMessage::addPaddingBytes(int bytes, uint8 byte)
m_messageSize += bytes; 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) if(m_messageSize < size)
throw stdext::exception("insufficient bytes in buffer to encrypt"); throw stdext::exception("insufficient bytes in buffer to encrypt");

View File

@ -49,7 +49,7 @@ public:
void addString(const std::string& buffer); void addString(const std::string& buffer);
void addPaddingBytes(int bytes, uint8 byte = 0); 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; } uint16 getMessageSize() { return m_messageSize; }

View File

@ -286,31 +286,27 @@ namespace Otc
}; };
enum GameFeature { enum GameFeature {
GameExtendedOpcode = 0, // 1-50 defined in c++
GameProtocolChecksum, GameProtocolChecksum = 1,
GameAccountNames, GameAccountNames = 2,
GameChallangeOnLogin, GameChallangeOnLogin = 3,
GameStackposOnTileAddThing, GamePenalityOnDeath = 5,
GamePenalityOnDeath, GameNameOnNpcTrade = 6,
GameNameOnNpcTrade, GameDoubleFreeCapacity = 7,
GameDoubleFreeCapacity, GameDoubleExperience = 8,
GameDoubleExperience, GameTotalCapacity = 9,
GameTotalCapacity, GameSkillsBase = 10,
GameSkillsBase, GamePlayerRegenerationTime = 11,
GameAdditionalPlayerStats, GameChannelPlayerList = 13,
GameIdOnCancelAttack, GamePlayerMounts = 14,
GameChannelPlayerList, GameEnvironmentEffect = 15,
GamePlayerMounts, GameCreatureEmblems = 17,
GameEnvironmentEffect, GameItemAnimationPhase = 19,
GameCreatureType, GameMagicEffectU16 = 22,
GameCreatureAdditionalInfo, GamePlayerMarket = 23,
GameCreaturePassableInfo, // 23-50 unused yet
GameItemAnimationPhase, // 51-100 reserved to be defined in lua
GameTrucatedPingOpcode, LastGameFeature = 101
GameReverseCreatureStack,
GameMagicEffectU16,
GamePlayerMarket,
LastGameFeature
}; };
enum PathFindResult { enum PathFindResult {

View File

@ -461,6 +461,8 @@ void Creature::setDirection(Otc::Direction direction)
void Creature::setOutfit(const Outfit& outfit) 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_walkAnimationPhase = 0; // might happen when player is walking and outfit is changed.
m_outfit = outfit; m_outfit = outfit;
} }

View File

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

View File

@ -38,7 +38,7 @@ Game g_game;
Game::Game() Game::Game()
{ {
resetGameStates(); resetGameStates();
setClientVersion(860); m_protocolVersion = 0;
} }
void Game::resetGameStates() 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) 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()) { if(m_protocolGame || isOnline())
g_logger.traceError("unable to login into a world while already online or logging"); stdext::throw_exception("Unable to login into a world while already online or logging.");
return;
} if(m_protocolVersion == 0)
stdext::throw_exception("Must set a valid game protocol version before logging.");
m_protocolGame = ProtocolGamePtr(new ProtocolGame); m_protocolGame = ProtocolGamePtr(new ProtocolGame);
m_protocolGame->login(account, password, worldHost, (uint16)worldPort, characterName); 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(); return m_localPlayer && !m_dead && m_protocolGame && m_protocolGame->isConnected() && checkBotProtection();
} }
void Game::setClientVersion(int clientVersion) void Game::setProtocolVersion(int version)
{ {
if(isOnline()) { if(isOnline())
g_logger.error("Unable to change client version while online"); stdext::throw_exception("Unable to change client version while online");
return;
}
//TODO: check supported versions if(version < 810 || version > 960)
stdext::throw_exception(stdext::format("Protocol version %d not supported", version));
m_features.reset(); m_features.reset();
if(clientVersion >= 854) { if(version >= 854) {
enableFeature(Otc::GameProtocolChecksum); enableFeature(Otc::GameProtocolChecksum);
enableFeature(Otc::GameAccountNames); enableFeature(Otc::GameAccountNames);
enableFeature(Otc::GameChallangeOnLogin); enableFeature(Otc::GameChallangeOnLogin);
enableFeature(Otc::GameStackposOnTileAddThing);
enableFeature(Otc::GameDoubleFreeCapacity); enableFeature(Otc::GameDoubleFreeCapacity);
enableFeature(Otc::GameCreatureAdditionalInfo); enableFeature(Otc::GameCreatureEmblems);
enableFeature(Otc::GameReverseCreatureStack);
} }
if(clientVersion >= 860) { if(version >= 862) {
enableFeature(Otc::GameIdOnCancelAttack);
}
if(clientVersion >= 862) {
enableFeature(Otc::GamePenalityOnDeath); enableFeature(Otc::GamePenalityOnDeath);
} }
if(clientVersion >= 870) { if(version >= 870) {
enableFeature(Otc::GameDoubleExperience); enableFeature(Otc::GameDoubleExperience);
enableFeature(Otc::GamePlayerMounts); enableFeature(Otc::GamePlayerMounts);
} }
if(clientVersion >= 910) { if(version >= 910) {
enableFeature(Otc::GameNameOnNpcTrade); enableFeature(Otc::GameNameOnNpcTrade);
enableFeature(Otc::GameTotalCapacity); enableFeature(Otc::GameTotalCapacity);
enableFeature(Otc::GameSkillsBase); enableFeature(Otc::GameSkillsBase);
enableFeature(Otc::GameAdditionalPlayerStats); enableFeature(Otc::GamePlayerRegenerationTime);
enableFeature(Otc::GameChannelPlayerList); enableFeature(Otc::GameChannelPlayerList);
enableFeature(Otc::GameEnvironmentEffect); enableFeature(Otc::GameEnvironmentEffect);
enableFeature(Otc::GameCreatureType);
enableFeature(Otc::GameItemAnimationPhase); enableFeature(Otc::GameItemAnimationPhase);
} }
if(clientVersion >= 940) { if(version >= 940) {
enableFeature(Otc::GamePlayerMarket); enableFeature(Otc::GamePlayerMarket);
} }
if(clientVersion >= 953) { m_protocolVersion = version;
enableFeature(Otc::GameCreaturePassableInfo);
enableFeature(Otc::GameTrucatedPingOpcode);
}
m_clientVersion = clientVersion;
} }
void Game::setAttackingCreature(const CreaturePtr& creature) void Game::setAttackingCreature(const CreaturePtr& creature)

View File

@ -232,8 +232,8 @@ public:
void setFeature(Otc::GameFeature feature) { m_features.set(feature, false); } void setFeature(Otc::GameFeature feature) { m_features.set(feature, false); }
bool getFeature(Otc::GameFeature feature) { return m_features.test(feature); } bool getFeature(Otc::GameFeature feature) { return m_features.test(feature); }
void setClientVersion(int clientVersion); void setProtocolVersion(int version);
int getClientVersion() { return m_clientVersion; } int getProtocolVersion() { return m_protocolVersion; }
void setRSA(const std::string& rsa); void setRSA(const std::string& rsa);
std::string getRSA() { return m_rsa; } std::string getRSA() { return m_rsa; }
@ -256,7 +256,6 @@ public:
int getServerBeat() { return m_serverBeat; } int getServerBeat() { return m_serverBeat; }
LocalPlayerPtr getLocalPlayer() { return m_localPlayer; } LocalPlayerPtr getLocalPlayer() { return m_localPlayer; }
ProtocolGamePtr getProtocolGame() { return m_protocolGame; } ProtocolGamePtr getProtocolGame() { return m_protocolGame; }
int getProtocolVersion() { return PROTOCOL; }
std::string getCharacterName() { return m_characterName; } std::string getCharacterName() { return m_characterName; }
std::string getWorldName() { return m_worldName; } std::string getWorldName() { return m_worldName; }
std::vector<uint8> getGMActions() { return m_gmActions; } std::vector<uint8> getGMActions() { return m_gmActions; }
@ -288,7 +287,7 @@ private:
std::string m_characterName; std::string m_characterName;
std::string m_worldName; std::string m_worldName;
std::bitset<Otc::LastGameFeature> m_features; std::bitset<Otc::LastGameFeature> m_features;
int m_clientVersion; int m_protocolVersion;
std::string m_rsa; std::string m_rsa;
}; };

View File

@ -101,7 +101,7 @@ void Item::draw(const Point& dest, float scaleFactor, bool animate)
else if(tile->mustHookEast()) else if(tile->mustHookEast())
xPattern = getNumPatternX() >= 3 ? 2 : 0; xPattern = getNumPatternX() >= 3 ? 2 : 0;
} }
} else if(isFluid() || isFluidContainer()) { } else if(isSplash() || isFluidContainer()) {
int color = Otc::FluidTransparent; int color = Otc::FluidTransparent;
switch(m_countOrSubType) { switch(m_countOrSubType) {
case Otc::FluidNone: case Otc::FluidNone:
@ -176,15 +176,20 @@ void Item::draw(const Point& dest, float scaleFactor, bool animate)
void Item::setId(uint32 id) 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_id = id;
m_otbId = 0;
} }
void Item::setOtbId(uint16 id) void Item::setOtbId(uint16 id)
{ {
if(!g_things.isValidOtbId(id))
id = 0;
auto otbType = g_things.getOtbType(id); auto otbType = g_things.getOtbType(id);
m_otbId = id;
m_id = otbType->getClientId(); m_id = otbType->getClientId();
m_otbId = id;
} }
bool Item::isValid() bool Item::isValid()

View File

@ -190,10 +190,10 @@ void OTClient::registerLuaFunctions()
g_lua.bindSingletonFunction("g_game", "getLocalPlayer", &Game::getLocalPlayer, &g_game); 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", "getProtocolGame", &Game::getProtocolGame, &g_game);
g_lua.bindSingletonFunction("g_game", "getProtocolVersion", &Game::getProtocolVersion, &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", "getCharacterName", &Game::getCharacterName, &g_game);
g_lua.bindSingletonFunction("g_game", "getWorldName", &Game::getWorldName, &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", "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.bindSingletonFunction("g_game", "getFeature", &Game::getFeature, &g_game);
g_lua.registerSingletonClass("g_shaders"); g_lua.registerSingletonClass("g_shaders");

View File

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

View File

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

View File

@ -25,7 +25,7 @@
Outfit::Outfit() Outfit::Outfit()
{ {
m_category = DatCreatureCategory; m_category = DatCreatureCategory;
m_id = 1; m_id = 0;
resetClothes(); resetClothes();
} }

View File

@ -25,49 +25,7 @@
#include "global.h" #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 { 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 { enum LoginServerOpts {
LoginServerError = 10, LoginServerError = 10,
LoginServerMotd = 20, LoginServerMotd = 20,

View File

@ -43,8 +43,11 @@ void ProtocolGame::parseMessage(const InputMessagePtr& msg)
opcode = msg->getU8(); opcode = msg->getU8();
// try to parse in lua first // try to parse in lua first
int readPos = msg->getReadPos();
if(callLuaField<bool>("onOpcode", opcode, msg)) if(callLuaField<bool>("onOpcode", opcode, msg))
continue; continue;
else
msg->setReadPos(readPos); // restore read pos
if(!m_gameInitialized && opcode > Proto::GameServerFirstGameOpcode) 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"); g_logger.warning("received a game opcode from the server, but the game is not initialized yet, this is a server side bug");
@ -66,16 +69,12 @@ void ProtocolGame::parseMessage(const InputMessagePtr& msg)
parseLoginWait(msg); parseLoginWait(msg);
break; break;
case Proto::GameServerPing: case Proto::GameServerPing:
if(g_game.getFeature(Otc::GameTrucatedPingOpcode))
parsePingBack(msg);
else
parsePing(msg);
break;
case Proto::GameServerPingBack: case Proto::GameServerPingBack:
if(g_game.getFeature(Otc::GameTrucatedPingOpcode)) if((opcode == Proto::GameServerPing && g_game.getProtocolVersion() >= 953) ||
parsePing(msg); (opcode == Proto::GameServerPingBack && g_game.getProtocolVersion() < 953))
else
parsePingBack(msg); parsePingBack(msg);
else
parsePing(msg);
break; break;
case Proto::GameServerChallange: case Proto::GameServerChallange:
parseChallange(msg); parseChallange(msg);
@ -334,9 +333,9 @@ void ProtocolGame::parseGMActions(const InputMessagePtr& msg)
int numViolationReasons; int numViolationReasons;
if(g_game.getClientVersion() >= 860) if(g_game.getProtocolVersion() >= 860)
numViolationReasons = 20; numViolationReasons = 20;
else if(g_game.getClientVersion() >= 854) else if(g_game.getProtocolVersion() >= 854)
numViolationReasons = 19; numViolationReasons = 19;
else else
numViolationReasons = 32; numViolationReasons = 32;
@ -454,7 +453,7 @@ void ProtocolGame::parseTileAddThing(const InputMessagePtr& msg)
Position pos = getPosition(msg); Position pos = getPosition(msg);
int stackPos = -1; int stackPos = -1;
if(g_game.getFeature(Otc::GameStackposOnTileAddThing)) if(g_game.getProtocolVersion() >= 854)
stackPos = msg->getU8(); stackPos = msg->getU8();
ThingPtr thing = getThing(msg); ThingPtr thing = getThing(msg);
@ -508,7 +507,7 @@ void ProtocolGame::parseCreatureMove(const InputMessagePtr& msg)
int stackPos = -2; int stackPos = -2;
// older protocols stores creatures in reverse order // older protocols stores creatures in reverse order
if(!g_game.getFeature(Otc::GameReverseCreatureStack)) if(!g_game.getProtocolVersion() >= 854)
stackPos = -1; stackPos = -1;
g_map.addThing(thing, newPos, stackPos); g_map.addThing(thing, newPos, stackPos);
@ -884,12 +883,11 @@ void ProtocolGame::parsePlayerStats(const InputMessagePtr& msg)
m_localPlayer->setStamina(stamina); m_localPlayer->setStamina(stamina);
m_localPlayer->setSoul(soul); m_localPlayer->setSoul(soul);
if(g_game.getFeature(Otc::GameAdditionalPlayerStats)) { if(g_game.getProtocolVersion() >= 910)
int speed = msg->getU16(); m_localPlayer->setSpeed(msg->getU16());
msg->getU16(); // regeneration time
m_localPlayer->setSpeed(speed); if(g_game.getFeature(Otc::GamePlayerRegenerationTime))
} msg->getU16(); // regeneration time
} }
void ProtocolGame::parsePlayerSkills(const InputMessagePtr& msg) void ProtocolGame::parsePlayerSkills(const InputMessagePtr& msg)
@ -913,7 +911,7 @@ void ProtocolGame::parsePlayerState(const InputMessagePtr& msg)
void ProtocolGame::parsePlayerCancelAttack(const InputMessagePtr& msg) void ProtocolGame::parsePlayerCancelAttack(const InputMessagePtr& msg)
{ {
if(g_game.getFeature(Otc::GameIdOnCancelAttack)) if(g_game.getProtocolVersion() >= 860)
msg->getU32(); // unknown msg->getU32(); // unknown
g_game.processAttackCancel(); g_game.processAttackCancel();
@ -1389,7 +1387,7 @@ CreaturePtr ProtocolGame::getCreature(const InputMessagePtr& msg, int type)
uint id = msg->getU32(); uint id = msg->getU32();
int creatureType; int creatureType;
if(g_game.getFeature(Otc::GameCreatureType)) if(g_game.getProtocolVersion() >= 910)
creatureType = msg->getU8(); creatureType = msg->getU8();
else { else {
if(id >= Proto::PlayerStartId && id < Proto::PlayerEndId) if(id >= Proto::PlayerStartId && id < Proto::PlayerEndId)
@ -1441,12 +1439,11 @@ CreaturePtr ProtocolGame::getCreature(const InputMessagePtr& msg, int type)
int emblem = -1; int emblem = -1;
bool passable = false; bool passable = false;
if(g_game.getFeature(Otc::GameCreatureAdditionalInfo)) { if(g_game.getFeature(Otc::GameCreatureEmblems) && !known)
if(!known) emblem = msg->getU8();
emblem = msg->getU8();
if(g_game.getProtocolVersion() >= 854)
passable = (msg->getU8() == 0); passable = (msg->getU8() == 0);
}
if(creature) { if(creature) {
creature->setHealthPercent(healthPercent); creature->setHealthPercent(healthPercent);
@ -1474,7 +1471,7 @@ CreaturePtr ProtocolGame::getCreature(const InputMessagePtr& msg, int type)
if(creature) if(creature)
creature->turn(direction); creature->turn(direction);
if(g_game.getFeature(Otc::GameCreaturePassableInfo)) { if(g_game.getProtocolVersion() >= 953) {
bool passable = msg->getU8(); bool passable = msg->getU8();
if(creature) if(creature)
@ -1497,7 +1494,7 @@ ItemPtr ProtocolGame::getItem(const InputMessagePtr& msg, int id)
if(item->getId() == 0) if(item->getId() == 0)
stdext::throw_exception("unable to create item with invalid id 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()); item->setCountOrSubType(msg->getU8());
if(g_game.getFeature(Otc::GameItemAnimationPhase)) { if(g_game.getFeature(Otc::GameItemAnimationPhase)) {

View File

@ -49,8 +49,9 @@ void ProtocolGame::sendLoginPacket(uint challangeTimestamp, uint8 challangeRando
OutputMessagePtr msg(new OutputMessage); OutputMessagePtr msg(new OutputMessage);
msg->addU8(Proto::ClientEnterGame); msg->addU8(Proto::ClientEnterGame);
msg->addU16(Proto::ClientOs);
msg->addU16(g_game.getClientVersion()); msg->addU16(g_lua.callGlobalField<int>("g_game", "getOs"));
msg->addU16(g_game.getProtocolVersion());
int paddingBytes = 128; int paddingBytes = 128;
msg->addU8(0); // first RSA byte must be 0 msg->addU8(0); // first RSA byte must be 0
@ -90,7 +91,7 @@ void ProtocolGame::sendLoginPacket(uint challangeTimestamp, uint8 challangeRando
msg->addPaddingBytes(paddingBytes); msg->addPaddingBytes(paddingBytes);
// encrypt with RSA // encrypt with RSA
msg->encryptRSA(128, Proto::RSA); msg->encryptRsa(128, g_lua.callGlobalField<std::string>("g_game", "getRsa"));
send(msg); send(msg);
@ -570,9 +571,10 @@ void ProtocolGame::sendShareExperience(bool active, int unknown)
OutputMessagePtr msg(new OutputMessage); OutputMessagePtr msg(new OutputMessage);
msg->addU8(Proto::ClientShareExperience); msg->addU8(Proto::ClientShareExperience);
msg->addU8(active ? 0x01 : 0x00); msg->addU8(active ? 0x01 : 0x00);
#if PROTOCOL<910
msg->addU8(unknown); if(g_game.getProtocolVersion() < 910)
#endif msg->addU8(unknown);
send(msg); send(msg);
} }

View File

@ -28,12 +28,6 @@
#include "thingtypemanager.h" #include "thingtypemanager.h"
#include <framework/luaengine/luaobject.h> #include <framework/luaengine/luaobject.h>
struct Light
{
uint8 intensity;
uint8 color;
};
// @bindclass // @bindclass
#pragma pack(push,1) // disable memory alignment #pragma pack(push,1) // disable memory alignment
class Thing : public LuaObject class Thing : public LuaObject
@ -95,8 +89,7 @@ public:
int getAnimationPhases() { return rawGetDatType()->getAnimationPhases(); } int getAnimationPhases() { return rawGetDatType()->getAnimationPhases(); }
int getGroundSpeed() { return rawGetDatType()->getGroundSpeed(); } int getGroundSpeed() { return rawGetDatType()->getGroundSpeed(); }
int getMaxTextLength() { return rawGetDatType()->getMaxTextLength(); } int getMaxTextLength() { return rawGetDatType()->getMaxTextLength(); }
int getLightLevel() { return rawGetDatType()->getLightLevel(); } Light getLight() { return rawGetDatType()->getLight(); }
int getLightColor() { return rawGetDatType()->getLightColor(); }
int getMinimapColor() { return rawGetDatType()->getMinimapColor(); } int getMinimapColor() { return rawGetDatType()->getMinimapColor(); }
int getLensHelp() { return rawGetDatType()->getLensHelp(); } int getLensHelp() { return rawGetDatType()->getLensHelp(); }
int getClothSlot() { return rawGetDatType()->getClothSlot(); } int getClothSlot() { return rawGetDatType()->getClothSlot(); }
@ -110,9 +103,10 @@ public:
bool isForceUse() { return rawGetDatType()->isForceUse(); } bool isForceUse() { return rawGetDatType()->isForceUse(); }
bool isMultiUse() { return rawGetDatType()->isMultiUse(); } bool isMultiUse() { return rawGetDatType()->isMultiUse(); }
bool isWritable() { return rawGetDatType()->isWritable(); } bool isWritable() { return rawGetDatType()->isWritable(); }
bool isChargeable() { return rawGetDatType()->isChargeable(); }
bool isWritableOnce() { return rawGetDatType()->isWritableOnce(); } bool isWritableOnce() { return rawGetDatType()->isWritableOnce(); }
bool isFluidContainer() { return rawGetDatType()->isFluidContainer(); } bool isFluidContainer() { return rawGetDatType()->isFluidContainer(); }
bool isFluid() { return rawGetDatType()->isFluid(); } bool isSplash() { return rawGetDatType()->isSplash(); }
bool isNotWalkable() { return rawGetDatType()->isNotWalkable(); } bool isNotWalkable() { return rawGetDatType()->isNotWalkable(); }
bool isNotMoveable() { return rawGetDatType()->isNotMoveable(); } bool isNotMoveable() { return rawGetDatType()->isNotMoveable(); }
bool blockProjectile() { return rawGetDatType()->blockProjectile(); } bool blockProjectile() { return rawGetDatType()->blockProjectile(); }
@ -134,6 +128,7 @@ public:
bool isFullGround() { return rawGetDatType()->isFullGround(); } bool isFullGround() { return rawGetDatType()->isFullGround(); }
bool isIgnoreLook() { return rawGetDatType()->isIgnoreLook(); } bool isIgnoreLook() { return rawGetDatType()->isIgnoreLook(); }
bool isCloth() { return rawGetDatType()->isCloth(); } bool isCloth() { return rawGetDatType()->isCloth(); }
MarketData getMarketData() { return rawGetDatType()->getMarketData(); }
protected: protected:
Position m_position; Position m_position;

View File

@ -22,6 +22,7 @@
#include "thingtypedat.h" #include "thingtypedat.h"
#include "spritemanager.h" #include "spritemanager.h"
#include "game.h"
#include <framework/graphics/graphics.h> #include <framework/graphics/graphics.h>
#include <framework/graphics/texture.h> #include <framework/graphics/texture.h>
@ -33,20 +34,11 @@ ThingTypeDat::ThingTypeDat()
{ {
m_category = DatInvalidCategory; m_category = DatInvalidCategory;
m_id = 0; m_id = 0;
m_null = true;
m_exactSize = 0; m_exactSize = 0;
m_layers = 0; m_numPatternX = m_numPatternY = m_numPatternZ = 0;
m_numPatternX = 0;
m_numPatternY = 0;
m_numPatternZ = 0;
m_animationPhases = 0; m_animationPhases = 0;
m_groundSpeed = 0; m_layers = 0;
m_maxTextLenght = 0;
m_lightLevel = 0;
m_lightColor = 0;
m_miniMapColor = 0;
m_lensHelp = 0;
m_clothSlot = 0;
m_elevation = 0;
} }
void ThingTypeDat::unserialize(uint16 clientId, DatCategory category, const FileStreamPtr& fin) 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_id = clientId;
m_category = category; m_category = category;
static int datVersion;
if(clientId == 100 && category == DatItemCategory)
datVersion = 2;
bool done = false; bool done = false;
for(int i = 0 ; i < DatLastAttrib;++i) { for(int i = 0 ; i < DatLastAttrib;++i) {
int property = fin->getU8(); int attrib = fin->getU8();
if(property == DatLastAttrib) { if(attrib == DatLastAttrib) {
done = true; done = true;
break; break;
} }
switch(property) { // hacky way to detect if is older dat version or not
case DatAttribIsGround: if(clientId == 100 && category == DatItemCategory && datVersion != 1 &&
m_isGround = true; (attrib == DatAttribNotPathable || attrib == DatAttribDontHide || attrib == DatAttribIgnoreLook)) {
m_groundSpeed = fin->getU16(); datVersion = 1;
if(m_groundSpeed == 0) }
m_groundSpeed = 100; 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; break;
case DatAttribIsGroundBorder: }
m_isGroundBorder = true; case DatAttribLight: {
Light light;
light.intensity = fin->getU16();
light.color = fin->getU16();
m_attribs.set(attrib, light);
break; break;
case DatAttribIsOnBottom: }
m_isOnBottom = true; case DatAttribMarket: {
break; MarketData market;
case DatAttribIsOnTop: market.category = fin->getU16();
m_isOnTop = true; market.showAs = fin->getU16();
break; market.tradeAs = fin->getU16();
case DatAttribIsContainer: market.name = fin->getString();
m_isContainer = true; market.restrictProfession = fin->getU16();
break; market.requiredLevel = fin->getU16();
case DatAttribIsStackable: m_attribs.set(attrib, market);
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();
break;
case DatAttribIsLyingCorpse:
m_isLyingCorpse = true;
break;
case DatAttribAnimateAlways:
m_isAnimateAlways = true;
break; break;
}
case DatAttribGround:
case DatAttribWritable:
case DatAttribWritableOnce:
case DatAttribElevation:
case DatAttribMiniMapColor: 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: case DatAttribCloth:
m_isCloth = true; case DatAttribLensHelp:
m_clothSlot = fin->getU16(); m_attribs.set(attrib, 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
break; break;
default: default:
stdext::throw_exception("corrupt data, invalid type attribute"); m_attribs.set(attrib, true);
break; break;
}; };
} }
@ -192,40 +116,18 @@ void ThingTypeDat::unserialize(uint16 clientId, DatCategory category, const File
if(!done) if(!done)
stdext::throw_exception("corrupt data"); stdext::throw_exception("corrupt data");
int totalSprites = 1; uint8 width = fin->getU8();
for(int i = 0; i < DatLastDimension; ++i) { uint8 height = fin->getU8();
switch(i) { m_size = Size(width, height);
case DatWidth: m_exactSize = (width > 1 || height > 1) ? std::min((int)fin->getU8(), std::max(width * 32, height * 32)) : 32;
m_size.setWidth(fin->getU8()); m_layers = fin->getU8();
break; m_numPatternX = fin->getU8();
case DatHeight: m_numPatternY = fin->getU8();
m_size.setHeight(fin->getU8()); m_numPatternZ = fin->getU8();
break; m_animationPhases = fin->getU8();
case DatExactSize:
if(m_size.width() <= 1 && m_size.height() <= 1) int totalSprites = m_size.area() * m_layers * m_numPatternX * m_numPatternY * m_numPatternZ * m_animationPhases;
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;
}
}
totalSprites = m_size.width() * m_size.height() * m_layers * m_numPatternX * m_numPatternY * m_numPatternZ * m_animationPhases;
if(totalSprites == 0) if(totalSprites == 0)
stdext::throw_exception("a thing type has no sprites"); stdext::throw_exception("a thing type has no sprites");
if(totalSprites > 4096) if(totalSprites > 4096)

View File

@ -29,6 +29,7 @@
#include <framework/graphics/coordsbuffer.h> #include <framework/graphics/coordsbuffer.h>
#include <framework/luaengine/luaobject.h> #include <framework/luaengine/luaobject.h>
#include <framework/net/server.h> #include <framework/net/server.h>
#include <framework/util/attribstorage.h>
enum DatCategory { enum DatCategory {
DatItemCategory = 0, DatItemCategory = 0,
@ -48,53 +49,59 @@ enum DatSpriteMask {
}; };
enum DatAttrib { enum DatAttrib {
DatAttribIsGround = 0, DatAttribGround = 0,
DatAttribIsGroundBorder, DatAttribGroundBorder,
DatAttribIsOnBottom, DatAttribOnBottom,
DatAttribIsOnTop, DatAttribOnTop,
DatAttribIsContainer, DatAttribContainer,
DatAttribIsStackable, DatAttribStackable,
DatAttribIsForceUse, DatAttribForceUse,
DatAttribIsMultiUse, DatAttribMultiUse,
DatAttribIsWritable, //DatAttribRune
DatAttribIsWritableOnce, DatAttribWritable,
DatAttribIsFluidContainer, DatAttribWritableOnce,
DatAttribIsFluid, DatAttribFluidContainer,
DatAttribIsNotWalkable, DatAttribSplash,
DatAttribIsNotMoveable, DatAttribNotWalkable,
DatAttribNotMoveable,
DatAttribBlockProjectile, DatAttribBlockProjectile,
DatAttribIsNotPathable, DatAttribNotPathable,
DatAttribIsPickupable, DatAttribPickupable,
DatAttribIsHangable, DatAttribHangable,
DatAttribHookSouth, DatAttribHookSouth,
DatAttribHookEast, DatAttribHookEast,
DatAttribIsRotateable, DatAttribRotateable,
DatAttribHasLight, DatAttribLight,
DatAttribDontHide, DatAttribDontHide,
DatAttribIsTranslucent, DatAttribTranslucent,
DatAttribHasDisplacement, DatAttribDisplacement,
DatAttribHasElevation, DatAttribElevation,
DatAttribIsLyingCorpse, DatAttribLyingCorpse,
DatAttribAnimateAlways, DatAttribAnimateAlways,
DatAttribMiniMapColor, DatAttribMiniMapColor,
DatAttribLensHelp, DatAttribLensHelp,
DatAttribIsFullGround, DatAttribFullGround,
DatAttribIgnoreLook, DatAttribIgnoreLook,
DatAttribCloth, DatAttribCloth,
DatAttribMarket, DatAttribMarket,
DatLastAttrib = 255 DatLastAttrib = 255,
// legacy attribs
DatAttribChargeable = 254
}; };
enum DatDimension { struct MarketData {
DatWidth = 0, std::string name;
DatHeight, int category;
DatExactSize, uint16 requiredLevel;
DatLayers, uint16 restrictProfession;
DatPatternX, uint16 showAs;
DatPatternY, uint16 tradeAs;
DatPatternZ, };
DatAnimationPhases,
DatLastDimension struct Light {
uint8 intensity;
uint8 color;
}; };
class ThingTypeDat : public LuaObject class ThingTypeDat : public LuaObject
@ -113,56 +120,58 @@ public:
Size getSize() { return m_size; } Size getSize() { return m_size; }
int getWidth() { return m_size.width(); } int getWidth() { return m_size.width(); }
int getHeight() { return m_size.height(); } 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 getExactSize() { return m_exactSize; }
int getLayers() { return m_layers; } int getLayers() { return m_layers; }
int getNumPatternX() { return m_numPatternX; } int getNumPatternX() { return m_numPatternX; }
int getNumPatternY() { return m_numPatternY; } int getNumPatternY() { return m_numPatternY; }
int getNumPatternZ() { return m_numPatternZ; } int getNumPatternZ() { return m_numPatternZ; }
int getAnimationPhases() { return m_animationPhases; } int getAnimationPhases() { return m_animationPhases; }
int getGroundSpeed() { return m_groundSpeed; } Point getDisplacement() { return m_displacement; }
int getMaxTextLength() { return m_maxTextLenght; } int getDisplacementX() { return getDisplacement().x; }
int getLightLevel() { return m_lightLevel; } int getDisplacementY() { return getDisplacement().y; }
int getLightColor() { return m_lightColor; }
int getMinimapColor() { return m_miniMapColor; } int getGroundSpeed() { return m_attribs.get<uint16>(DatAttribGround); }
int getLensHelp() { return m_lensHelp; } int getMaxTextLength() { return m_attribs.has(DatAttribWritableOnce) ? m_attribs.get<uint16>(DatAttribWritableOnce) : m_attribs.get<uint16>(DatAttribWritable); }
int getClothSlot() { return m_clothSlot; } Light getLight() { return m_attribs.get<Light>(DatAttribLight); }
int getElevation() { return m_elevation; } int getMinimapColor() { return m_attribs.get<uint16>(DatAttribMiniMapColor); }
bool isGround() { return m_isGround; } int getLensHelp() { return m_attribs.get<uint16>(DatAttribLensHelp); }
bool isGroundBorder() { return m_isGroundBorder; } int getClothSlot() { return m_attribs.get<uint16>(DatAttribCloth); }
bool isOnBottom() { return m_isOnBottom; } int getElevation() { return m_attribs.get<uint16>(DatAttribElevation); }
bool isOnTop() { return m_isOnTop; } MarketData getMarketData() { return m_attribs.get<MarketData>(DatAttribMarket); }
bool isContainer() { return m_isContainer; } bool isGround() { return m_attribs.has(DatAttribGround); }
bool isStackable() { return m_isStackable; } bool isGroundBorder() { return m_attribs.has(DatAttribGroundBorder); }
bool isForceUse() { return m_isForceUse; } bool isOnBottom() { return m_attribs.has(DatAttribOnBottom); }
bool isMultiUse() { return m_isMultiUse; } bool isOnTop() { return m_attribs.has(DatAttribOnTop); }
bool isWritable() { return m_isWritable; } bool isContainer() { return m_attribs.has(DatAttribContainer); }
bool isWritableOnce() { return m_isWritableOnce; } bool isStackable() { return m_attribs.has(DatAttribStackable); }
bool isFluidContainer() { return m_isFluidContainer; } bool isForceUse() { return m_attribs.has(DatAttribForceUse); }
bool isFluid() { return m_isFluid; } bool isMultiUse() { return m_attribs.has(DatAttribMultiUse); }
bool isNotWalkable() { return m_isNotWalkable; } bool isWritable() { return m_attribs.has(DatAttribWritable); }
bool isNotMoveable() { return m_isNotMoveable; } bool isChargeable() { return m_attribs.has(DatAttribChargeable); }
bool blockProjectile() { return m_blockProjectile; } bool isWritableOnce() { return m_attribs.has(DatAttribWritableOnce); }
bool isNotPathable() { return m_isNotPathable; } bool isFluidContainer() { return m_attribs.has(DatAttribFluidContainer); }
bool isPickupable() { return m_isPickupable; } bool isSplash() { return m_attribs.has(DatAttribSplash); }
bool isHangable() { return m_isHangable; } bool isNotWalkable() { return m_attribs.has(DatAttribNotWalkable); }
bool isHookSouth() { return m_isHookSouth; } bool isNotMoveable() { return m_attribs.has(DatAttribNotMoveable); }
bool isHookEast() { return m_isHookEast; } bool blockProjectile() { return m_attribs.has(DatAttribBlockProjectile); }
bool isRotateable() { return m_isRotateable; } bool isNotPathable() { return m_attribs.has(DatAttribNotPathable); }
bool hasLight() { return m_hasLight; } bool isPickupable() { return m_attribs.has(DatAttribPickupable); }
bool isDontHide() { return m_isDontHide; } bool isHangable() { return m_attribs.has(DatAttribHangable); }
bool isTranslucent() { return m_isTranslucent; } bool isHookSouth() { return m_attribs.has(DatAttribHookSouth); }
bool hasDisplacement() { return m_hasDisplacement; } bool isHookEast() { return m_attribs.has(DatAttribHookEast); }
bool hasElevation() { return m_hasElevation; } bool isRotateable() { return m_attribs.has(DatAttribRotateable); }
bool isLyingCorpse() { return m_isLyingCorpse; } bool hasLight() { return m_attribs.has(DatAttribLight); }
bool isAnimateAlways() { return m_isAnimateAlways; } bool isDontHide() { return m_attribs.has(DatAttribDontHide); }
bool hasMiniMapColor() { return m_hasMiniMapColor; } bool isTranslucent() { return m_attribs.has(DatAttribTranslucent); }
bool hasLensHelp() { return m_hasLensHelp; } bool hasDisplacement() { return m_attribs.has(DatAttribDisplacement); }
bool isFullGround() { return m_isFullGround; } bool hasElevation() { return m_attribs.has(DatAttribElevation); }
bool isIgnoreLook() { return m_isIgnoreLook; } bool isLyingCorpse() { return m_attribs.has(DatAttribLyingCorpse); }
bool isCloth() { return m_isCloth; } 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: private:
const TexturePtr& getTexture(int animationPhase); const TexturePtr& getTexture(int animationPhase);
@ -172,64 +181,21 @@ private:
DatCategory m_category; DatCategory m_category;
uint16 m_id; uint16 m_id;
Boolean<true> 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<int> m_spritesIndex; std::vector<int> m_spritesIndex;
std::vector<TexturePtr> m_textures; std::vector<TexturePtr> m_textures;
std::vector<std::vector<Rect>> m_texturesFramesRects; std::vector<std::vector<Rect>> m_texturesFramesRects;
std::vector<std::vector<Rect>> m_texturesFramesOriginRects; std::vector<std::vector<Rect>> m_texturesFramesOriginRects;
std::vector<std::vector<Point>> m_texturesFramesOffsets; std::vector<std::vector<Point>> 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<false> m_isGround;
Boolean<false> m_isGroundBorder;
Boolean<false> m_isOnBottom;
Boolean<false> m_isOnTop;
Boolean<false> m_isContainer;
Boolean<false> m_isStackable;
Boolean<false> m_isForceUse;
Boolean<false> m_isMultiUse;
Boolean<false> m_isWritable;
Boolean<false> m_isWritableOnce;
Boolean<false> m_isFluidContainer;
Boolean<false> m_isFluid;
Boolean<false> m_isNotWalkable;
Boolean<false> m_isNotMoveable;
Boolean<false> m_blockProjectile;
Boolean<false> m_isNotPathable;
Boolean<false> m_isPickupable;
Boolean<false> m_isHangable;
Boolean<false> m_isHookSouth;
Boolean<false> m_isHookEast;
Boolean<false> m_isRotateable;
Boolean<false> m_hasLight;
Boolean<false> m_isDontHide;
Boolean<false> m_isTranslucent;
Boolean<false> m_hasDisplacement;
Boolean<false> m_hasElevation;
Boolean<false> m_isLyingCorpse;
Boolean<false> m_isAnimateAlways;
Boolean<false> m_hasMiniMapColor;
Boolean<false> m_hasLensHelp;
Boolean<false> m_isFullGround;
Boolean<false> m_isIgnoreLook;
Boolean<false> m_isCloth;
}; };
#endif #endif

View File

@ -367,7 +367,7 @@ ThingPtr Tile::getTopMultiUseThing()
for(uint i = 0; i < m_things.size(); ++i) { for(uint i = 0; i < m_things.size(); ++i) {
ThingPtr thing = m_things[i]; ThingPtr thing = m_things[i];
if(thing->isForceUse() || (!thing->isGround() && !thing->isGroundBorder() && !thing->isOnBottom() && !thing->isOnTop())) { 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 m_things[i-1];
return thing; return thing;
} }

View File

@ -58,7 +58,7 @@ void UIItem::drawSelf(Fw::DrawPane drawPane)
g_painter->setColor(Color::white); g_painter->setColor(Color::white);
m_item->draw(dest, scaleFactor, true); 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()); std::string count = stdext::to_string(m_item->getCount());
g_painter->setColor(Color(231, 231, 231)); g_painter->setColor(Color(231, 231, 231));
m_font->drawText(count, Rect(m_rect.topLeft(), m_rect.bottomRight() - Point(3, 0)), Fw::AlignBottomRight); m_font->drawText(count, Rect(m_rect.topLeft(), m_rect.bottomRight() - Point(3, 0)), Fw::AlignBottomRight);