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
9
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)
|
||||
|
|
|
@ -16,7 +16,6 @@ Module
|
|||
- client_terminal
|
||||
- client_modulemanager
|
||||
- client_entergame
|
||||
- game
|
||||
|
||||
@onLoad: |
|
||||
dofile 'client'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -55,4 +55,4 @@ function Extended.unregister(opcode)
|
|||
|
||||
callbacks[opcode] = nil
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -4,9 +4,6 @@ Module
|
|||
author: BeniS
|
||||
website: www.otclient.info
|
||||
|
||||
dependencies:
|
||||
- game
|
||||
|
||||
@onLoad: |
|
||||
dofile 'marketoffer'
|
||||
dofile 'marketprotocol'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
-- @}
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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'
|
Before Width: | Height: | Size: 385 B After Width: | Height: | Size: 385 B |
Before Width: | Height: | Size: 381 B After Width: | Height: | Size: 381 B |
Before Width: | Height: | Size: 386 B After Width: | Height: | Size: 386 B |
Before Width: | Height: | Size: 352 B After Width: | Height: | Size: 352 B |
Before Width: | Height: | Size: 522 B After Width: | Height: | Size: 522 B |
Before Width: | Height: | Size: 516 B After Width: | Height: | Size: 516 B |
Before Width: | Height: | Size: 404 B After Width: | Height: | Size: 404 B |
Before Width: | Height: | Size: 377 B After Width: | Height: | Size: 377 B |
Before Width: | Height: | Size: 512 B After Width: | Height: | Size: 512 B |
Before Width: | Height: | Size: 494 B After Width: | Height: | Size: 494 B |
Before Width: | Height: | Size: 407 B After Width: | Height: | Size: 407 B |
Before Width: | Height: | Size: 482 B After Width: | Height: | Size: 482 B |
Before Width: | Height: | Size: 438 B After Width: | Height: | Size: 438 B |
Before Width: | Height: | Size: 445 B After Width: | Height: | Size: 445 B |
Before Width: | Height: | Size: 421 B After Width: | Height: | Size: 421 B |
Before Width: | Height: | Size: 437 B After Width: | Height: | Size: 437 B |
Before Width: | Height: | Size: 437 B After Width: | Height: | Size: 437 B |
|
@ -52,4 +52,3 @@ function Player:hasVip(creatureName)
|
|||
end
|
||||
return false
|
||||
end
|
||||
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -141,3 +141,14 @@ void Application::close()
|
|||
if(!g_lua.callGlobalField<bool>("g_app", "onClose"))
|
||||
exit();
|
||||
}
|
||||
|
||||
std::string Application::getOs()
|
||||
{
|
||||
#if defined(WIN32)
|
||||
return "windows";
|
||||
#elif defined(__APPLE__)
|
||||
return "mac";
|
||||
#else
|
||||
return "linux";
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -22,22 +22,25 @@
|
|||
|
||||
#include <framework/core/application.h>
|
||||
#include <framework/luaengine/luainterface.h>
|
||||
#include <framework/graphics/fontmanager.h>
|
||||
#include <framework/ui/ui.h>
|
||||
#include <framework/net/protocol.h>
|
||||
#include <framework/core/eventdispatcher.h>
|
||||
#include <framework/core/configmanager.h>
|
||||
#include <framework/otml/otml.h>
|
||||
#include <framework/graphics/graphics.h>
|
||||
#include <framework/platform/platformwindow.h>
|
||||
#include <framework/core/modulemanager.h>
|
||||
#include <framework/core/module.h>
|
||||
#include <framework/sound/soundmanager.h>
|
||||
#include <framework/util/crypt.h>
|
||||
#include <framework/core/resourcemanager.h>
|
||||
#include <framework/graphics/particlemanager.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()
|
||||
{
|
||||
// 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", "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", "getOs", &Application::getOs, static_cast<Application*>(&g_app));
|
||||
g_lua.bindSingletonFunction("g_app", "exit", &Application::exit, static_cast<Application*>(&g_app));
|
||||
|
||||
// Crypt
|
||||
|
@ -644,7 +648,7 @@ void Application::registerLuaFunctions()
|
|||
g_lua.bindClassMemberFunction<InputMessage>("peekU16", &InputMessage::peekU16);
|
||||
g_lua.bindClassMemberFunction<InputMessage>("peekU32", &InputMessage::peekU32);
|
||||
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>("getUnreadSize", &InputMessage::getUnreadSize);
|
||||
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>("addString", &OutputMessage::addString);
|
||||
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);
|
||||
#endif
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -46,6 +46,8 @@ void Effect::startAnimation()
|
|||
|
||||
void Effect::setId(uint32 id)
|
||||
{
|
||||
if(!g_things.isValidDatId(id, DatEffectCategory))
|
||||
id = 0;
|
||||
m_id = id;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
enableFeature(Otc::GameCreatureEmblems);
|
||||
}
|
||||
|
||||
if(clientVersion >= 860) {
|
||||
enableFeature(Otc::GameIdOnCancelAttack);
|
||||
}
|
||||
|
||||
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)
|
||||
|
|
|
@ -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<uint8> getGMActions() { return m_gmActions; }
|
||||
|
@ -288,7 +287,7 @@ private:
|
|||
std::string m_characterName;
|
||||
std::string m_worldName;
|
||||
std::bitset<Otc::LastGameFeature> m_features;
|
||||
int m_clientVersion;
|
||||
int m_protocolVersion;
|
||||
std::string m_rsa;
|
||||
};
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
Outfit::Outfit()
|
||||
{
|
||||
m_category = DatCreatureCategory;
|
||||
m_id = 1;
|
||||
m_id = 0;
|
||||
resetClothes();
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<bool>("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,16 +69,12 @@ void ProtocolGame::parseMessage(const InputMessagePtr& msg)
|
|||
parseLoginWait(msg);
|
||||
break;
|
||||
case Proto::GameServerPing:
|
||||
if(g_game.getFeature(Otc::GameTrucatedPingOpcode))
|
||||
parsePingBack(msg);
|
||||
else
|
||||
parsePing(msg);
|
||||
break;
|
||||
case Proto::GameServerPingBack:
|
||||
if(g_game.getFeature(Otc::GameTrucatedPingOpcode))
|
||||
parsePing(msg);
|
||||
else
|
||||
if((opcode == Proto::GameServerPing && g_game.getProtocolVersion() >= 953) ||
|
||||
(opcode == Proto::GameServerPingBack && g_game.getProtocolVersion() < 953))
|
||||
parsePingBack(msg);
|
||||
else
|
||||
parsePing(msg);
|
||||
break;
|
||||
case Proto::GameServerChallange:
|
||||
parseChallange(msg);
|
||||
|
@ -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)) {
|
||||
|
|
|
@ -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<int>("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<std::string>("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);
|
||||
}
|
||||
|
||||
|
|
|
@ -28,12 +28,6 @@
|
|||
#include "thingtypemanager.h"
|
||||
#include <framework/luaengine/luaobject.h>
|
||||
|
||||
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;
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include "thingtypedat.h"
|
||||
#include "spritemanager.h"
|
||||
#include "game.h"
|
||||
|
||||
#include <framework/graphics/graphics.h>
|
||||
#include <framework/graphics/texture.h>
|
||||
|
@ -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;
|
||||
// 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 DatAttribIsGroundBorder:
|
||||
m_isGroundBorder = true;
|
||||
}
|
||||
case DatAttribLight: {
|
||||
Light light;
|
||||
light.intensity = fin->getU16();
|
||||
light.color = fin->getU16();
|
||||
m_attribs.set(attrib, light);
|
||||
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();
|
||||
break;
|
||||
case DatAttribIsLyingCorpse:
|
||||
m_isLyingCorpse = true;
|
||||
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)
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include <framework/graphics/coordsbuffer.h>
|
||||
#include <framework/luaengine/luaobject.h>
|
||||
#include <framework/net/server.h>
|
||||
#include <framework/util/attribstorage.h>
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
enum DatDimension {
|
||||
DatWidth = 0,
|
||||
DatHeight,
|
||||
DatExactSize,
|
||||
DatLayers,
|
||||
DatPatternX,
|
||||
DatPatternY,
|
||||
DatPatternZ,
|
||||
DatAnimationPhases,
|
||||
DatLastDimension
|
||||
struct MarketData {
|
||||
std::string name;
|
||||
int category;
|
||||
uint16 requiredLevel;
|
||||
uint16 restrictProfession;
|
||||
uint16 showAs;
|
||||
uint16 tradeAs;
|
||||
};
|
||||
|
||||
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<uint16>(DatAttribGround); }
|
||||
int getMaxTextLength() { return m_attribs.has(DatAttribWritableOnce) ? m_attribs.get<uint16>(DatAttribWritableOnce) : m_attribs.get<uint16>(DatAttribWritable); }
|
||||
Light getLight() { return m_attribs.get<Light>(DatAttribLight); }
|
||||
int getMinimapColor() { return m_attribs.get<uint16>(DatAttribMiniMapColor); }
|
||||
int getLensHelp() { return m_attribs.get<uint16>(DatAttribLensHelp); }
|
||||
int getClothSlot() { return m_attribs.get<uint16>(DatAttribCloth); }
|
||||
int getElevation() { return m_attribs.get<uint16>(DatAttribElevation); }
|
||||
MarketData getMarketData() { return m_attribs.get<MarketData>(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<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<TexturePtr> m_textures;
|
||||
std::vector<std::vector<Rect>> m_texturesFramesRects;
|
||||
std::vector<std::vector<Rect>> m_texturesFramesOriginRects;
|
||||
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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|