More multiprotocol support

This commit is contained in:
Eduardo Bart 2012-07-26 03:10:28 -03:00
parent e393bc245d
commit c795eb91ab
43 changed files with 421 additions and 341 deletions

View File

@ -92,7 +92,7 @@ end
local function onLoginWait(message, time) local function onLoginWait(message, time)
CharacterList.destroyLoadBox() CharacterList.destroyLoadBox()
waitingWindow = g_ui.loadUI('waitinglist.otui') waitingWindow = g_ui.displayUI('waitinglist.otui')
local label = waitingWindow:getChildById('infoLabel') local label = waitingWindow:getChildById('infoLabel')
label:setText(message) label:setText(message)

View File

@ -168,7 +168,8 @@ function EnterGame.doLogin()
end }) end })
g_game.chooseRsa(G.host) g_game.chooseRsa(G.host)
g_game.setProtocolVersion(protocol) g_game.setClientVersion(protocol)
modules.game_tibiafiles.load()
protocolLogin:login(G.host, G.port, G.account, G.password) protocolLogin:login(G.host, G.port, G.account, G.password)
end end

View File

@ -72,7 +72,14 @@ function connect(object, arg1, arg2, arg3)
end end
end end
function disconnect(object, signalsAndSlots) function disconnect(object, arg1, arg2)
local signalsAndSlots
if type(arg1) == 'string' then
signalsAndSlots = { [arg1] = arg2 }
else
signalsAndSlots = arg1
end
for signal,slot in pairs(signalsAndSlots) do for signal,slot in pairs(signalsAndSlots) do
if not object[signal] then if not object[signal] then
elseif type(object[signal]) == 'function' then elseif type(object[signal]) == 'function' then
@ -231,11 +238,19 @@ end
function signalcall(param, ...) function signalcall(param, ...)
if type(param) == 'function' then if type(param) == 'function' then
return param(...) local status, ret = pcall(param, ...)
if status then
return ret
else
perror(ret)
end
elseif type(param) == 'table' then elseif type(param) == 'table' then
for k,v in pairs(param) do for k,v in pairs(param) do
if v(...) then local status, ret = pcall(v, ...)
return true if status then
if ret then return true end
else
perror(ret)
end end
end end
elseif func ~= nil then elseif func ~= nil then

View File

@ -91,7 +91,7 @@ end
function addAllCreatures() function addAllCreatures()
local spectators = {} local spectators = {}
local player = g_game.getLocalPlayer() local player = g_game.getLocalPlayer()
if player then if g_game.isOnline() then
creatures = g_map.getSpectators(player:getPosition(), false) creatures = g_map.getSpectators(player:getPosition(), false)
for i, creature in ipairs(creatures) do for i, creature in ipairs(creatures) do
if creature ~= player and doCreatureFitFilters(creature) then if creature ~= player and doCreatureFitFilters(creature) then
@ -129,7 +129,7 @@ end
function checkCreatures(forceRecheck) function checkCreatures(forceRecheck)
local player = g_game.getLocalPlayer() local player = g_game.getLocalPlayer()
if player then if g_game.isOnline() then
local spectators = {} local spectators = {}
-- reloading list of spectators -- reloading list of spectators

View File

@ -135,10 +135,6 @@ function toggle()
end end
end end
function onMiniWindowClose()
combatControlsButton:setOn(false)
end
function onSetFightMode(self, selectedFightButton) function onSetFightMode(self, selectedFightButton)
if selectedFightButton == nil then return end if selectedFightButton == nil then return end
local buttonId = selectedFightButton:getId() local buttonId = selectedFightButton:getId()

View File

@ -27,7 +27,6 @@ MiniWindow
!text: tr('Combat Controls') !text: tr('Combat Controls')
icon: combatcontrols.png icon: combatcontrols.png
height: 48 height: 48
@onClose: CombatControls.onMiniWindowClose()
&save: true &save: true
MiniWindowContents MiniWindowContents

View File

@ -37,14 +37,14 @@ function clean()
end end
end end
local function refreshContainerItems(container) function refreshContainerItems(container)
for slot=0,container:getCapacity()-1 do for slot=0,container:getCapacity()-1 do
local itemWidget = container.itemsPanel:getChildById('item' .. slot) local itemWidget = container.itemsPanel:getChildById('item' .. slot)
itemWidget:setItem(container:getItem(slot)) itemWidget:setItem(container:getItem(slot))
end end
end end
local function onContainerOpen(container, previousContainer) function onContainerOpen(container, previousContainer)
local containerWindow local containerWindow
if previousContainer then if previousContainer then
containerWindow = previousContainer.window containerWindow = previousContainer.window

View File

@ -2,7 +2,7 @@ Icons = {}
Icons[1] = { tooltip = tr('You are poisoned'), path = '/game_healthinfo/icons/poisoned.png', id = 'condition_poisoned' } Icons[1] = { tooltip = tr('You are poisoned'), path = '/game_healthinfo/icons/poisoned.png', id = 'condition_poisoned' }
Icons[2] = { tooltip = tr('You are burning'), path = '/game_healthinfo/icons/burning.png', id = 'condition_burning' } Icons[2] = { tooltip = tr('You are burning'), path = '/game_healthinfo/icons/burning.png', id = 'condition_burning' }
Icons[4] = { tooltip = tr('You are electrified'), path = '/game_healthinfo/icons/electrified.png', id = 'condition_electrified' } Icons[4] = { tooltip = tr('You are electrified'), path = '/game_healthinfo/icons/electrified.png', id = 'condition_electrified' }
Icons[8] = { tooltip = tr('You are freezing'), path = '/game_healthinfo/icons/drunk.png', id = 'condition_drunk' } Icons[8] = { tooltip = tr('You are drunk'), path = '/game_healthinfo/icons/drunk.png', id = 'condition_drunk' }
Icons[16] = { tooltip = tr('You are protected by a magic shield'), path = '/game_healthinfo/icons/magic_shield.png', id = 'condition_magic_shield' } Icons[16] = { tooltip = tr('You are protected by a magic shield'), path = '/game_healthinfo/icons/magic_shield.png', id = 'condition_magic_shield' }
Icons[32] = { tooltip = tr('You are paralysed'), path = '/game_healthinfo/icons/slowed.png', id = 'condition_slowed' } Icons[32] = { tooltip = tr('You are paralysed'), path = '/game_healthinfo/icons/slowed.png', id = 'condition_slowed' }
Icons[64] = { tooltip = tr('You are hasted'), path = '/game_healthinfo/icons/haste.png', id = 'condition_haste' } Icons[64] = { tooltip = tr('You are hasted'), path = '/game_healthinfo/icons/haste.png', id = 'condition_haste' }

View File

@ -238,6 +238,7 @@ function startTradeWith(thing)
end end
function createThingMenu(menuPosition, lookThing, useThing, creatureThing) function createThingMenu(menuPosition, lookThing, useThing, creatureThing)
if not g_game.isOnline() then return end
local menu = g_ui.createWidget('PopupMenu') local menu = g_ui.createWidget('PopupMenu')
if lookThing then if lookThing then
@ -297,52 +298,50 @@ function createThingMenu(menuPosition, lookThing, useThing, creatureThing)
else else
local localPlayer = g_game.getLocalPlayer() local localPlayer = g_game.getLocalPlayer()
if localPlayer then if g_game.getAttackingCreature() ~= creatureThing then
if g_game.getAttackingCreature() ~= creatureThing then menu:addOption(tr('Attack'), function() g_game.attack(creatureThing) end)
menu:addOption(tr('Attack'), function() g_game.attack(creatureThing) end) else
else menu:addOption(tr('Stop Attack'), function() g_game.cancelAttack() end)
menu:addOption(tr('Stop Attack'), function() g_game.cancelAttack() end) end
if g_game.getFollowingCreature() ~= creatureThing then
menu:addOption(tr('Follow'), function() g_game.follow(creatureThing) end)
else
menu:addOption(tr('Stop Follow'), function() g_game.cancelFollow() end)
end
if creatureThing:asPlayer() then
menu:addSeparator()
local creatureName = creatureThing:getName()
menu:addOption(tr('Message to %s', creatureName), function() g_game.openPrivateChannel(creatureName) end)
if modules.game_console.getOwnPrivateTab() then
menu:addOption(tr('Invite to private chat'), function() g_game.inviteToOwnChannel(creatureName) end)
menu:addOption(tr('Exclude from private chat'), function() g_game.excludeFromOwnChannel(creatureName) end) -- [TODO] must be removed after message's popup labels been implemented
end
if (not Player:hasVip(creatureName)) then
menu:addOption(tr('Add to VIP list'), function() g_game.addVip(creatureName) end)
end end
if g_game.getFollowingCreature() ~= creatureThing then local localPlayerShield = localPlayer:asCreature():getShield()
menu:addOption(tr('Follow'), function() g_game.follow(creatureThing) end) local creatureShield = creatureThing:getShield()
else
menu:addOption(tr('Stop Follow'), function() g_game.cancelFollow() end)
end
if creatureThing:asPlayer() then if localPlayerShield == ShieldNone or localPlayerShield == ShieldWhiteBlue then
menu:addSeparator() if creatureShield == ShieldWhiteYellow then
local creatureName = creatureThing:getName() menu:addOption(tr('Join %s\'s Party', creatureThing:getName()), function() g_game.partyJoin(creatureThing:getId()) end)
menu:addOption(tr('Message to %s', creatureName), function() g_game.openPrivateChannel(creatureName) end) else
if modules.game_console.getOwnPrivateTab() then menu:addOption(tr('Invite to Party'), function() g_game.partyInvite(creatureThing:getId()) end)
menu:addOption(tr('Invite to private chat'), function() g_game.inviteToOwnChannel(creatureName) end)
menu:addOption(tr('Exclude from private chat'), function() g_game.excludeFromOwnChannel(creatureName) end) -- [TODO] must be removed after message's popup labels been implemented
end end
if (not Player:hasVip(creatureName)) then elseif localPlayerShield == ShieldWhiteYellow then
menu:addOption(tr('Add to VIP list'), function() g_game.addVip(creatureName) end) if creatureShield == ShieldWhiteBlue then
menu:addOption(tr('Revoke %s\'s Invitation', creatureThing:getName()), function() g_game.partyRevokeInvitation(creatureThing:getId()) end)
end end
elseif localPlayerShield == ShieldYellow or localPlayerShield == ShieldYellowSharedExp or localPlayerShield == ShieldYellowNoSharedExpBlink or localPlayerShield == ShieldYellowNoSharedExp then
local localPlayerShield = localPlayer:asCreature():getShield() if creatureShield == ShieldWhiteBlue then
local creatureShield = creatureThing:getShield() menu:addOption(tr('Revoke %s\'s Invitation', creatureThing:getName()), function() g_game.partyRevokeInvitation(creatureThing:getId()) end)
elseif creatureShield == ShieldBlue or creatureShield == ShieldBlueSharedExp or creatureShield == ShieldBlueNoSharedExpBlink or creatureShield == ShieldBlueNoSharedExp then
if localPlayerShield == ShieldNone or localPlayerShield == ShieldWhiteBlue then menu:addOption(tr('Pass Leadership to %s', creatureThing:getName()), function() g_game.partyPassLeadership(creatureThing:getId()) end)
if creatureShield == ShieldWhiteYellow then else
menu:addOption(tr('Join %s\'s Party', creatureThing:getName()), function() g_game.partyJoin(creatureThing:getId()) end) menu:addOption(tr('Invite to Party'), function() g_game.partyInvite(creatureThing:getId()) end)
else
menu:addOption(tr('Invite to Party'), function() g_game.partyInvite(creatureThing:getId()) end)
end
elseif localPlayerShield == ShieldWhiteYellow then
if creatureShield == ShieldWhiteBlue then
menu:addOption(tr('Revoke %s\'s Invitation', creatureThing:getName()), function() g_game.partyRevokeInvitation(creatureThing:getId()) end)
end
elseif localPlayerShield == ShieldYellow or localPlayerShield == ShieldYellowSharedExp or localPlayerShield == ShieldYellowNoSharedExpBlink or localPlayerShield == ShieldYellowNoSharedExp then
if creatureShield == ShieldWhiteBlue then
menu:addOption(tr('Revoke %s\'s Invitation', creatureThing:getName()), function() g_game.partyRevokeInvitation(creatureThing:getId()) end)
elseif creatureShield == ShieldBlue or creatureShield == ShieldBlueSharedExp or creatureShield == ShieldBlueNoSharedExpBlink or creatureShield == ShieldBlueNoSharedExp then
menu:addOption(tr('Pass Leadership to %s', creatureThing:getName()), function() g_game.partyPassLeadership(creatureThing:getId()) end)
else
menu:addOption(tr('Invite to Party'), function() g_game.partyInvite(creatureThing:getId()) end)
end
end end
end end
end end

View File

@ -43,7 +43,7 @@ end
function refresh() function refresh()
local player = g_game.getLocalPlayer() local player = g_game.getLocalPlayer()
for i=InventorySlotFirst,InventorySlotLast do for i=InventorySlotFirst,InventorySlotLast do
if player then if g_game.isOnline() then
onInventoryChange(player, i, player:getInventoryItem(i)) onInventoryChange(player, i, player:getInventoryItem(i))
else else
onInventoryChange(player, i, nil) onInventoryChange(player, i, nil)
@ -67,8 +67,9 @@ end
-- hooked events -- hooked events
function onInventoryChange(player, slot, item, oldItem) function onInventoryChange(player, slot, item, oldItem)
if slot >= InventorySlotPurse then return end
local itemWidget = inventoryPanel:getChildById('slot' .. slot) local itemWidget = inventoryPanel:getChildById('slot' .. slot)
if(item) then if item then
itemWidget:setStyle('Item') itemWidget:setStyle('Item')
itemWidget:setItem(item) itemWidget:setItem(item)
else else

View File

@ -38,7 +38,7 @@ end
local function parseMarketEnter(msg) local function parseMarketEnter(msg)
local balance = msg:getU32() local balance = msg:getU32()
local vocation = -1 local vocation = -1
if g_game.getProtocolVersion() < 950 then if g_game.getClientVersion() < 950 then
vocation = msg:getU8() -- get vocation id vocation = msg:getU8() -- get vocation id
end end
local offers = msg:getU8() local offers = msg:getU8()
@ -74,7 +74,8 @@ local function parseMarketDetail(msg)
end end
local purchaseStats = {} local purchaseStats = {}
if msg:getU8() == 0x01 then local count = msg:getU8()
for i=1,count do
local transactions = msg:getU32() -- transaction count local transactions = msg:getU32() -- transaction count
local totalPrice = msg:getU32() -- total price local totalPrice = msg:getU32() -- total price
local highestPrice = msg:getU32() -- highest price local highestPrice = msg:getU32() -- highest price
@ -84,7 +85,8 @@ local function parseMarketDetail(msg)
end end
local saleStats = {} local saleStats = {}
if msg:getU8() == 0x01 then count = msg:getU8()
for i=1,count do
local transactions = msg:getU32() -- transaction count local transactions = msg:getU32() -- transaction count
local totalPrice = msg:getU32() -- total price local totalPrice = msg:getU32() -- total price
local highestPrice = msg:getU32() -- highest price local highestPrice = msg:getU32() -- highest price

View File

@ -67,6 +67,7 @@ function loadReasons()
local actions = g_game.getGMActions() local actions = g_game.getGMActions()
for reason, actionFlags in pairs(actions) do for reason, actionFlags in pairs(actions) do
local label = g_ui.createWidget('RVListLabel', reasonsTextList) local label = g_ui.createWidget('RVListLabel', reasonsTextList)
label.onFocusChange = onSelectReason
label:setText(rvreasons[reason]) label:setText(rvreasons[reason])
label.reasonId = reason label.reasonId = reason
label.actionFlags = actionFlags label.actionFlags = actionFlags

View File

@ -2,7 +2,6 @@ RVListLabel < Label
background-color: alpha background-color: alpha
text-offset: 2 0 text-offset: 2 0
focusable: true focusable: true
@onFocusChange: function (self, focused) RuleViolation.onSelectReason(self, focused) end
$focus: $focus:
background-color: #ffffff22 background-color: #ffffff22
@ -109,7 +108,7 @@ MainWindow
width: 64 width: 64
anchors.right: parent.right anchors.right: parent.right
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
@onClick: RuleViolation.hide() @onClick: hide()
Button Button
!text: tr('Ok') !text: tr('Ok')
@ -117,4 +116,4 @@ MainWindow
margin-right: 5 margin-right: 5
anchors.right: prev.left anchors.right: prev.left
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
@onClick: RuleViolation.report() @onClick: report()

View File

@ -47,7 +47,7 @@ end
function parseTextMessage(msg) function parseTextMessage(msg)
local msgtype = msg:getU8() local msgtype = msg:getU8()
local text = msg:getString() local text = msg:getString()
msgtype = getMessageTypes(g_game.getProtocolVersion())[msgtype] msgtype = getMessageTypes(g_game.getClientVersion())[msgtype]
signalcall(g_game.onTextMessage, msgtype, text) signalcall(g_game.onTextMessage, msgtype, text)
end end

View File

@ -1,11 +1,17 @@
function init() function init()
if not g_things.loadDat('/game_tibiafiles/Tibia.dat') then if g_game.getClientVersion() ~= 0 then
fatal(tr("Unable to load dat file, please place a valid Tibia dat in modules/game_tibiafiles/Tibia.dat")) load()
end
if not g_sprites.loadSpr('/game_tibiafiles/Tibia.spr') then
fatal(tr("Unable to load spr file, please place a valid Tibia spr in modules/game_tibiafiles/Tibia.spr"))
end end
end end
function terminate() function load()
local version = g_game.getClientVersion()
local datPath = resolvepath(version .. '/Tibia.dat')
local sprPath = resolvepath(version .. '/Tibia.spr')
if not g_things.loadDat(datPath) then
fatal(tr("Unable to load dat file, please place a valid dat in '%s'", datPath))
end
if not g_sprites.loadSpr(sprPath) then
fatal(tr("Unable to load spr file, please place a valid spr in '%s'", sprPath))
end
end end

View File

@ -71,6 +71,9 @@ CIPSOFT_RSA = "1321277432058722840622950990822933849527763264961655079678763618"
"2907336840325241747827401343576296990629870233111328210165697754" .. "2907336840325241747827401343576296990629870233111328210165697754" ..
"88792221429527047321331896351555606801473202394175817" "88792221429527047321331896351555606801473202394175817"
-- set to the latest Tibia.pic signature to make otclient compatible with official tibia
PIC_SIGNATURE = 1337606793
OsTypes = { OsTypes = {
Linux = 1, Linux = 1,
Windows = 2, Windows = 2,

View File

@ -11,6 +11,7 @@ InventorySlotLeg = 7
InventorySlotFeet = 8 InventorySlotFeet = 8
InventorySlotFinger = 9 InventorySlotFinger = 9
InventorySlotAmmo = 10 InventorySlotAmmo = 10
InventorySlotPurse = 11
InventorySlotFirst = 1 InventorySlotFirst = 1
InventorySlotLast = 10 InventorySlotLast = 10

View File

@ -1,89 +1,11 @@
-- @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 = 1337606793
LoginServerError = 10 LoginServerError = 10
LoginServerMotd = 20 LoginServerMotd = 20
LoginServerUpdateNeeded = 30 LoginServerUpdateNeeded = 30
LoginServerCharacterList = 100 LoginServerCharacterList = 100
-- private functions
local function sendLoginPacket(protocol)
local msg = OutputMessage.create()
msg:addU8(ClientOpcodes.ClientEnterAccount)
msg:addU16(g_game.getOsType())
msg:addU16(g_game.getProtocolVersion())
msg:addU32(g_things.getDatSignature())
msg:addU32(g_sprites.getSprSignature())
msg:addU32(PIC_SIGNATURE)
local paddingBytes = 128
msg:addU8(0) -- first RSA byte must be 0
paddingBytes = paddingBytes - 1
-- xtea key
protocol:generateXteaKey()
local xteaKey = protocol:getXteaKey()
msg:addU32(xteaKey[1])
msg:addU32(xteaKey[2])
msg:addU32(xteaKey[3])
msg:addU32(xteaKey[4])
paddingBytes = paddingBytes - 16
if g_game.getFeature(GameProtocolChecksum) then
protocol:enableChecksum()
end
if g_game.getFeature(GameAccountNames) then
msg:addString(protocol.accountName)
msg:addString(protocol.accountPassword)
paddingBytes = paddingBytes - (4 + string.len(protocol.accountName) + string.len(protocol.accountPassword))
else
msg:addU32(tonumber(protocol.accountName))
msg:addString(protocol.accountPassword)
paddingBytes = paddingBytes - (6 + string.len(protocol.accountPassword))
end
msg:addPaddingBytes(paddingBytes, 0)
msg:encryptRsa(128, g_game.getRsa())
protocol:send(msg)
protocol:enableXteaEncryption()
protocol:recv()
end
-- events
function ProtocolLogin:onConnect()
self:connectCallback(self)
end
function ProtocolLogin:onRecv(msg)
while not msg:eof() do
local opcode = msg:getU8()
if opcode == LoginServerError then
self:parseError(msg)
elseif opcode == LoginServerMotd then
self:parseMotd(msg)
elseif opcode == LoginServerUpdateNeeded then
signalcall(self.onError, self, tr("Client needs update."))
elseif opcode == LoginServerCharacterList then
self:parseCharacterList(msg)
else
self:parseOpcode(opcode, msg)
end
end
self:disconnect()
end
-- public functions
function ProtocolLogin.create()
return ProtocolLogin.internalCreate()
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, tr("You must enter an account name and password.")) signalcall(self.onError, self, tr("You must enter an account name and password."))
@ -105,6 +27,73 @@ function ProtocolLogin:cancelLogin()
self:disconnect() self:disconnect()
end end
function ProtocolLogin:sendLoginPacket()
local msg = OutputMessage.create()
msg:addU8(ClientOpcodes.ClientEnterAccount)
msg:addU16(g_game.getOsType())
msg:addU16(g_game.getClientVersion())
msg:addU32(g_things.getDatSignature())
msg:addU32(g_sprites.getSprSignature())
msg:addU32(PIC_SIGNATURE)
local paddingBytes = 128
msg:addU8(0) -- first RSA byte must be 0
paddingBytes = paddingBytes - 1
-- xtea key
self:generateXteaKey()
local xteaKey = self:getXteaKey()
msg:addU32(xteaKey[1])
msg:addU32(xteaKey[2])
msg:addU32(xteaKey[3])
msg:addU32(xteaKey[4])
paddingBytes = paddingBytes - 16
if g_game.getFeature(GameProtocolChecksum) then
self:enableChecksum()
end
if g_game.getFeature(GameAccountNames) then
msg:addString(self.accountName)
msg:addString(self.accountPassword)
paddingBytes = paddingBytes - (4 + string.len(self.accountName) + string.len(self.accountPassword))
else
msg:addU32(tonumber(self.accountName))
msg:addString(self.accountPassword)
paddingBytes = paddingBytes - (6 + string.len(self.accountPassword))
end
msg:addPaddingBytes(paddingBytes, 0)
msg:encryptRsa(128, g_game.getRsa())
self:send(msg)
self:enableXteaEncryption()
self:recv()
end
function ProtocolLogin:onConnect()
self:sendLoginPacket()
end
function ProtocolLogin:onRecv(msg)
while not msg:eof() do
local opcode = msg:getU8()
if opcode == LoginServerError then
self:parseError(msg)
elseif opcode == LoginServerMotd then
self:parseMotd(msg)
elseif opcode == LoginServerUpdateNeeded then
signalcall(self.onError, self, tr("Client needs update."))
elseif opcode == LoginServerCharacterList then
self:parseCharacterList(msg)
else
self:parseOpcode(opcode, msg)
end
end
self:disconnect()
end
function ProtocolLogin:parseError(msg) function ProtocolLogin:parseError(msg)
local errorMessage = msg:getString() local errorMessage = msg:getString()
signalcall(self.onError, self, errorMessage) signalcall(self.onError, self, errorMessage)

View File

@ -37,14 +37,14 @@ static const std::string glslMainWithTexCoordsVertexShader = "\n\
void main()\n\ void main()\n\
{\n\ {\n\
gl_Position = calculatePosition();\n\ gl_Position = calculatePosition();\n\
v_TexCoord = (u_TextureMatrix * vec3(a_TexCoord,1)).xy;\n\ v_TexCoord = (u_TextureMatrix * vec3(a_TexCoord,1.0)).xy;\n\
}\n"; }\n";
static std::string glslPositionOnlyVertexShader = "\n\ static std::string glslPositionOnlyVertexShader = "\n\
attribute highp vec2 a_Vertex;\n\ attribute highp vec2 a_Vertex;\n\
uniform highp mat3 u_ProjectionMatrix;\n\ uniform highp mat3 u_ProjectionMatrix;\n\
highp vec4 calculatePosition() {\n\ highp vec4 calculatePosition() {\n\
return vec4(u_ProjectionMatrix * vec3(a_Vertex.xy, 1), 1);\n\ return vec4(u_ProjectionMatrix * vec3(a_Vertex.xy, 1.0), 1.0);\n\
}\n"; }\n";
static const std::string glslMainFragmentShader = "\n\ static const std::string glslMainFragmentShader = "\n\

View File

@ -336,6 +336,7 @@ void Application::registerLuaFunctions()
g_lua.bindClassMemberFunction<UIWidget>("getChildByIndex", &UIWidget::getChildByIndex); g_lua.bindClassMemberFunction<UIWidget>("getChildByIndex", &UIWidget::getChildByIndex);
g_lua.bindClassMemberFunction<UIWidget>("recursiveGetChildById", &UIWidget::recursiveGetChildById); g_lua.bindClassMemberFunction<UIWidget>("recursiveGetChildById", &UIWidget::recursiveGetChildById);
g_lua.bindClassMemberFunction<UIWidget>("recursiveGetChildByPos", &UIWidget::recursiveGetChildByPos); g_lua.bindClassMemberFunction<UIWidget>("recursiveGetChildByPos", &UIWidget::recursiveGetChildByPos);
g_lua.bindClassMemberFunction<UIWidget>("recursiveGetChildren", &UIWidget::recursiveGetChildren);
g_lua.bindClassMemberFunction<UIWidget>("recursiveGetChildrenByPos", &UIWidget::recursiveGetChildrenByPos); g_lua.bindClassMemberFunction<UIWidget>("recursiveGetChildrenByPos", &UIWidget::recursiveGetChildrenByPos);
g_lua.bindClassMemberFunction<UIWidget>("recursiveGetChildrenByMarginPos", &UIWidget::recursiveGetChildrenByMarginPos); g_lua.bindClassMemberFunction<UIWidget>("recursiveGetChildrenByMarginPos", &UIWidget::recursiveGetChildrenByMarginPos);
g_lua.bindClassMemberFunction<UIWidget>("backwardsGetWidgetById", &UIWidget::backwardsGetWidgetById); g_lua.bindClassMemberFunction<UIWidget>("backwardsGetWidgetById", &UIWidget::backwardsGetWidgetById);

View File

@ -56,7 +56,7 @@ private:
SoundSourcePtr createSoundSource(const std::string& filename); SoundSourcePtr createSoundSource(const std::string& filename);
uint loadFileIntoBuffer(const SoundFilePtr& soundFile); uint loadFileIntoBuffer(const SoundFilePtr& soundFile);
std::map<std::string, SoundBufferPtr> m_buffers; std::unordered_map<std::string, SoundBufferPtr> m_buffers;
std::vector<SoundSourcePtr> m_sources; std::vector<SoundSourcePtr> m_sources;
SoundSourcePtr m_musicSource; SoundSourcePtr m_musicSource;
ALCdevice *m_device; ALCdevice *m_device;

View File

@ -81,7 +81,7 @@ protected:
private: private:
bool updateWidget(const UIWidgetPtr& widget, UIAnchorGroup& anchorGroup, UIWidgetPtr first = nullptr); bool updateWidget(const UIWidgetPtr& widget, UIAnchorGroup& anchorGroup, UIWidgetPtr first = nullptr);
std::map<UIWidgetPtr, UIAnchorGroup> m_anchorsGroups; std::unordered_map<UIWidgetPtr, UIAnchorGroup> m_anchorsGroups;
}; };
#endif #endif

View File

@ -1130,6 +1130,18 @@ UIWidgetPtr UIWidget::recursiveGetChildByPos(const Point& childPos, bool wantsPh
return nullptr; return nullptr;
} }
UIWidgetList UIWidget::recursiveGetChildren()
{
UIWidgetList children;
for(const UIWidgetPtr& child : m_children) {
UIWidgetList subChildren = child->recursiveGetChildren();
if(!subChildren.empty())
children.insert(children.end(), subChildren.begin(), subChildren.end());
children.push_back(child);
}
return children;
}
UIWidgetList UIWidget::recursiveGetChildrenByPos(const Point& childPos) UIWidgetList UIWidget::recursiveGetChildrenByPos(const Point& childPos)
{ {
UIWidgetList children; UIWidgetList children;

View File

@ -147,6 +147,7 @@ public:
UIWidgetPtr getChildByIndex(int index); UIWidgetPtr getChildByIndex(int index);
UIWidgetPtr recursiveGetChildById(const std::string& id); UIWidgetPtr recursiveGetChildById(const std::string& id);
UIWidgetPtr recursiveGetChildByPos(const Point& childPos, bool wantsPhantom); UIWidgetPtr recursiveGetChildByPos(const Point& childPos, bool wantsPhantom);
UIWidgetList recursiveGetChildren();
UIWidgetList recursiveGetChildrenByPos(const Point& childPos); UIWidgetList recursiveGetChildrenByPos(const Point& childPos);
UIWidgetList recursiveGetChildrenByMarginPos(const Point& childPos); UIWidgetList recursiveGetChildrenByMarginPos(const Point& childPos);
UIWidgetPtr backwardsGetWidgetById(const std::string& id); UIWidgetPtr backwardsGetWidgetById(const std::string& id);

View File

@ -79,7 +79,7 @@ public:
uint8 getSkull() { return m_skull; } uint8 getSkull() { return m_skull; }
uint8 getShield() { return m_shield; } uint8 getShield() { return m_shield; }
uint8 getEmblem() { return m_emblem; } uint8 getEmblem() { return m_emblem; }
bool getPassable() { return m_passable; } bool isPassable() { return m_passable; }
Point getDrawOffset(); Point getDrawOffset();
Point getWalkOffset() { return m_walkOffset; } Point getWalkOffset() { return m_walkOffset; }

View File

@ -39,15 +39,16 @@ Game::Game()
{ {
resetGameStates(); resetGameStates();
m_protocolVersion = 0; m_protocolVersion = 0;
setProtocolVersion(PROTOCOL);
} }
void Game::resetGameStates() void Game::resetGameStates()
{ {
m_online = false;
m_denyBotCall = false; m_denyBotCall = false;
m_dead = false; m_dead = false;
m_mounted = false; m_mounted = false;
m_serverBeat = 50; m_serverBeat = 50;
m_seq = 0;
m_canReportBugs = false; m_canReportBugs = false;
m_fightMode = Otc::FightBalanced; m_fightMode = Otc::FightBalanced;
m_chaseMode = Otc::DontChase; m_chaseMode = Otc::DontChase;
@ -104,15 +105,9 @@ void Game::processLoginWait(const std::string& message, int time)
g_lua.callGlobalField("g_game", "onLoginWait", message, time); g_lua.callGlobalField("g_game", "onLoginWait", message, time);
} }
void Game::processGameStart(const LocalPlayerPtr& localPlayer, int serverBeat, bool canReportBugs) void Game::processGameStart()
{ {
// reset the new game state m_online = true;
resetGameStates();
m_localPlayer = localPlayer;
m_localPlayer->setName(m_characterName);
m_serverBeat = serverBeat;
m_canReportBugs = canReportBugs;
// synchronize fight modes with the server // synchronize fight modes with the server
m_protocolGame->sendChangeFightModes(m_fightMode, m_chaseMode, m_safeFight); m_protocolGame->sendChangeFightModes(m_fightMode, m_chaseMode, m_safeFight);
@ -401,9 +396,9 @@ void Game::processQuestLine(int questId, const std::vector<std::tuple<std::strin
g_lua.callGlobalField("g_game", "onQuestLine", questId, questMissions); g_lua.callGlobalField("g_game", "onQuestLine", questId, questMissions);
} }
void Game::processAttackCancel() void Game::processAttackCancel(uint seq)
{ {
if(isAttacking()) if(isAttacking() && (seq == 0 || m_seq == seq))
setAttackingCreature(nullptr); setAttackingCreature(nullptr);
} }
@ -423,6 +418,12 @@ void Game::loginWorld(const std::string& account, const std::string& password, c
if(m_protocolVersion == 0) if(m_protocolVersion == 0)
stdext::throw_exception("Must set a valid game protocol version before logging."); stdext::throw_exception("Must set a valid game protocol version before logging.");
// reset the new game state
resetGameStates();
m_localPlayer = LocalPlayerPtr(new LocalPlayer);
m_localPlayer->setName(m_characterName);
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);
m_characterName = characterName; m_characterName = characterName;
@ -505,15 +506,10 @@ void Game::autoWalk(const std::vector<Otc::Direction>& dirs)
Otc::Direction direction = dirs.front(); Otc::Direction direction = dirs.front();
TilePtr toTile = g_map.getTile(m_localPlayer->getPosition().translatedToDirection(direction)); TilePtr toTile = g_map.getTile(m_localPlayer->getPosition().translatedToDirection(direction));
if(toTile && toTile->isWalkable()) if(toTile && toTile->isWalkable() && !m_localPlayer->isAutoWalking())
m_localPlayer->preWalk(direction); m_localPlayer->preWalk(direction);
forceWalk(direction); m_protocolGame->sendAutoWalk(dirs);
std::vector<Otc::Direction> nextDirs = dirs;
nextDirs.erase(nextDirs.begin());
if(nextDirs.size() > 0)
m_protocolGame->sendAutoWalk(nextDirs);
g_lua.callGlobalField("g_game", "onAutoWalk", direction); g_lua.callGlobalField("g_game", "onAutoWalk", direction);
} }
@ -737,7 +733,7 @@ void Game::attack(const CreaturePtr& creature)
cancelFollow(); cancelFollow();
setAttackingCreature(creature); setAttackingCreature(creature);
m_protocolGame->sendAttack(creature ? creature->getId() : 0); m_protocolGame->sendAttack(creature ? creature->getId() : 0, ++m_seq);
} }
void Game::follow(const CreaturePtr& creature) void Game::follow(const CreaturePtr& creature)
@ -751,7 +747,7 @@ void Game::follow(const CreaturePtr& creature)
cancelAttack(); cancelAttack();
setFollowingCreature(creature); setFollowingCreature(creature);
m_protocolGame->sendFollow(creature ? creature->getId() : 0); m_protocolGame->sendFollow(creature ? creature->getId() : 0, ++m_seq);
} }
void Game::cancelAttackAndFollow() void Game::cancelAttackAndFollow()
@ -885,7 +881,7 @@ void Game::partyShareExperience(bool active)
{ {
if(!canPerformGameAction()) if(!canPerformGameAction())
return; return;
m_protocolGame->sendShareExperience(active, 0); m_protocolGame->sendShareExperience(active);
} }
void Game::requestOutfit() void Game::requestOutfit()
@ -1084,15 +1080,16 @@ bool Game::checkBotProtection()
bool Game::canPerformGameAction() bool Game::canPerformGameAction()
{ {
// we can only perform game actions if we meet these conditions: // we can only perform game actions if we meet these conditions:
// - the game is online
// - the local player exists // - the local player exists
// - the local player is not dead // - the local player is not dead
// - we have a game protocol // - we have a game protocol
// - the game protocol is connected // - the game protocol is connected
// - its not a bot action // - its not a bot action
return m_localPlayer && !m_dead && m_protocolGame && m_protocolGame->isConnected() && checkBotProtection(); return m_online && m_localPlayer && !m_dead && m_protocolGame && m_protocolGame->isConnected() && checkBotProtection();
} }
void Game::setProtocolVersion(int version) void Game::setClientVersion(int version)
{ {
if(isOnline()) if(isOnline())
stdext::throw_exception("Unable to change client version while online"); stdext::throw_exception("Unable to change client version while online");
@ -1147,6 +1144,8 @@ void Game::setProtocolVersion(int version)
} }
m_protocolVersion = version; m_protocolVersion = version;
g_lua.callGlobalField("g_game", "onClientVersionChange", version);
} }
void Game::setAttackingCreature(const CreaturePtr& creature) void Game::setAttackingCreature(const CreaturePtr& creature)

View File

@ -48,7 +48,7 @@ protected:
void processLoginAdvice(const std::string& message); void processLoginAdvice(const std::string& message);
void processLoginWait(const std::string& message, int time); void processLoginWait(const std::string& message, int time);
void processGameStart(const LocalPlayerPtr& localPlayer, int serverBeat, bool canReportBugs); void processGameStart();
void processGameEnd(); void processGameEnd();
void processDeath(int penality); void processDeath(int penality);
@ -56,7 +56,7 @@ protected:
void processInventoryChange(int slot, const ItemPtr& item); void processInventoryChange(int slot, const ItemPtr& item);
void processCreatureMove(const CreaturePtr& creature, const Position& oldPos, const Position& newPos); void processCreatureMove(const CreaturePtr& creature, const Position& oldPos, const Position& newPos);
void processCreatureTeleport(const CreaturePtr& creature); void processCreatureTeleport(const CreaturePtr& creature);
void processAttackCancel(); void processAttackCancel(uint seq);
void processWalkCancel(Otc::Direction direction); void processWalkCancel(Otc::Direction direction);
// message related // message related
@ -232,17 +232,16 @@ 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 setProtocolVersion(int version); void setClientVersion(int version);
int getProtocolVersion() { return m_protocolVersion; } int getClientVersion() { 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; }
bool canPerformGameAction(); bool canPerformGameAction();
bool canReportBugs() { return m_canReportBugs; }
bool checkBotProtection(); bool checkBotProtection();
bool isOnline() { return !!m_localPlayer; } bool isOnline() { return m_online; }
bool isDead() { return m_dead; } bool isDead() { return m_dead; }
bool isAttacking() { return !!m_attackingCreature; } bool isAttacking() { return !!m_attackingCreature; }
bool isFollowing() { return !!m_followingCreature; } bool isFollowing() { return !!m_followingCreature; }
@ -253,7 +252,10 @@ public:
std::map<int, Vip> getVips() { return m_vips; } std::map<int, Vip> getVips() { return m_vips; }
CreaturePtr getAttackingCreature() { return m_attackingCreature; } CreaturePtr getAttackingCreature() { return m_attackingCreature; }
CreaturePtr getFollowingCreature() { return m_followingCreature; } CreaturePtr getFollowingCreature() { return m_followingCreature; }
void setServerBeat(int beat) { m_serverBeat = beat; }
int getServerBeat() { return m_serverBeat; } int getServerBeat() { return m_serverBeat; }
void setCanReportBugs(bool enable) { m_canReportBugs = enable; }
bool canReportBugs() { return m_canReportBugs; }
LocalPlayerPtr getLocalPlayer() { return m_localPlayer; } LocalPlayerPtr getLocalPlayer() { return m_localPlayer; }
ProtocolGamePtr getProtocolGame() { return m_protocolGame; } ProtocolGamePtr getProtocolGame() { return m_protocolGame; }
std::string getCharacterName() { return m_characterName; } std::string getCharacterName() { return m_characterName; }
@ -275,10 +277,12 @@ private:
std::map<int, ContainerPtr> m_containers; std::map<int, ContainerPtr> m_containers;
std::map<int, Vip> m_vips; std::map<int, Vip> m_vips;
bool m_online;
bool m_denyBotCall; bool m_denyBotCall;
bool m_dead; bool m_dead;
bool m_mounted; bool m_mounted;
int m_serverBeat; int m_serverBeat;
uint m_seq;
Otc::FightModes m_fightMode; Otc::FightModes m_fightMode;
Otc::ChaseModes m_chaseMode; Otc::ChaseModes m_chaseMode;
bool m_safeFight; bool m_safeFight;

View File

@ -187,8 +187,8 @@ void Item::setOtbId(uint16 id)
{ {
if(!g_things.isValidOtbId(id)) if(!g_things.isValidOtbId(id))
id = 0; id = 0;
auto otbType = g_things.getItemType(id); auto itemType = g_things.getItemType(id);
m_id = otbType->getClientId(); m_id = itemType->getClientId();
m_otbId = id; m_otbId = id;
} }

View File

@ -194,8 +194,8 @@ void OTClient::registerLuaFunctions()
g_lua.bindSingletonFunction("g_game", "getServerBeat", &Game::getServerBeat, &g_game); g_lua.bindSingletonFunction("g_game", "getServerBeat", &Game::getServerBeat, &g_game);
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", "getClientVersion", &Game::getClientVersion, &g_game);
g_lua.bindSingletonFunction("g_game", "setProtocolVersion", &Game::setProtocolVersion, &g_game); g_lua.bindSingletonFunction("g_game", "setClientVersion", &Game::setClientVersion, &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);

View File

@ -518,7 +518,7 @@ void Map::saveOtcm(const std::string& fileName)
// version 1 header // version 1 header
fin->addString("OTCM 1.0"); // map description fin->addString("OTCM 1.0"); // map description
fin->addU32(g_things.getDatSignature()); fin->addU32(g_things.getDatSignature());
fin->addU16(g_game.getProtocolVersion()); fin->addU16(g_game.getClientVersion());
fin->addString(g_game.getWorldName()); fin->addString(g_game.getWorldName());
// go back and rewrite where the map data starts // go back and rewrite where the map data starts

View File

@ -175,7 +175,7 @@ public:
private: private:
TileMap m_tiles; TileMap m_tiles;
std::map<uint32, CreaturePtr> m_knownCreatures; std::unordered_map<uint32, CreaturePtr> m_knownCreatures;
std::array<std::vector<MissilePtr>, Otc::MAX_Z+1> m_floorMissiles; std::array<std::vector<MissilePtr>, Otc::MAX_Z+1> m_floorMissiles;
std::vector<AnimatedTextPtr> m_animatedTexts; std::vector<AnimatedTextPtr> m_animatedTexts;
std::vector<StaticTextPtr> m_staticTexts; std::vector<StaticTextPtr> m_staticTexts;

View File

@ -81,6 +81,6 @@ void OTClient::terminate()
{ {
g_map.terminate(); g_map.terminate();
g_things.terminate(); g_things.terminate();
g_sprites.termiante(); g_sprites.terminate();
g_shaders.terminate(); g_shaders.terminate();
} }

View File

@ -135,7 +135,8 @@ namespace Proto {
GameServerMarketEnter = 246, // 944 GameServerMarketEnter = 246, // 944
GameServerMarketLeave = 247, // 944 GameServerMarketLeave = 247, // 944
GameServerMarketDetail = 248, // 944 GameServerMarketDetail = 248, // 944
GameServerMarketBrowse = 249 // 944 GameServerMarketBrowse = 249, // 944
GameServerShowModalDialog = 250 // 960
}; };
enum ClientOpcodes { enum ClientOpcodes {

View File

@ -44,8 +44,7 @@ void ProtocolGame::onConnect()
{ {
Protocol::onConnect(); Protocol::onConnect();
// must create local player before parsing anything m_localPlayer = g_game.getLocalPlayer();
m_localPlayer = LocalPlayerPtr(new LocalPlayer);
if(g_game.getFeature(Otc::GameProtocolChecksum)) if(g_game.getFeature(Otc::GameProtocolChecksum))
enableChecksum(); enableChecksum();

View File

@ -79,14 +79,14 @@ public:
void sendOpenPrivateChannel(const std::string& receiver); void sendOpenPrivateChannel(const std::string& receiver);
void sendCloseNpcChannel(); void sendCloseNpcChannel();
void sendChangeFightModes(Otc::FightModes fightMode, Otc::ChaseModes chaseMode, bool safeFight); void sendChangeFightModes(Otc::FightModes fightMode, Otc::ChaseModes chaseMode, bool safeFight);
void sendAttack(uint creatureId); void sendAttack(uint creatureId, uint seq);
void sendFollow(uint creatureId); void sendFollow(uint creatureId, uint seq);
void sendInviteToParty(uint creatureId); void sendInviteToParty(uint creatureId);
void sendJoinParty(uint creatureId); void sendJoinParty(uint creatureId);
void sendRevokeInvitation(uint creatureId); void sendRevokeInvitation(uint creatureId);
void sendPassLeadership(uint creatureId); void sendPassLeadership(uint creatureId);
void sendLeaveParty(); void sendLeaveParty();
void sendShareExperience(bool active, int unknown); void sendShareExperience(bool active);
void sendOpenOwnChannel(); void sendOpenOwnChannel();
void sendInviteToOwnChannel(const std::string& name); void sendInviteToOwnChannel(const std::string& name);
void sendExcludeFromOwnChannel(const std::string& name); void sendExcludeFromOwnChannel(const std::string& name);
@ -166,9 +166,9 @@ private:
void parsePlayerSkills(const InputMessagePtr& msg); void parsePlayerSkills(const InputMessagePtr& msg);
void parsePlayerState(const InputMessagePtr& msg); void parsePlayerState(const InputMessagePtr& msg);
void parsePlayerCancelAttack(const InputMessagePtr& msg); void parsePlayerCancelAttack(const InputMessagePtr& msg);
void parseSpellDelay(const InputMessagePtr& msg); void parseSpellCooldown(const InputMessagePtr& msg);
void parseSpellGroupDelay(const InputMessagePtr& msg); void parseSpellGroupCooldown(const InputMessagePtr& msg);
void parseMultiUseDelay(const InputMessagePtr& msg); void parseMultiUseCooldown(const InputMessagePtr& msg);
void parseCreatureSpeak(const InputMessagePtr& msg); void parseCreatureSpeak(const InputMessagePtr& msg);
void parseChannelList(const InputMessagePtr& msg); void parseChannelList(const InputMessagePtr& msg);
void parseOpenChannel(const InputMessagePtr& msg); void parseOpenChannel(const InputMessagePtr& msg);
@ -198,15 +198,17 @@ private:
void parseChannelEvent(const InputMessagePtr& msg); void parseChannelEvent(const InputMessagePtr& msg);
void parseItemInfo(const InputMessagePtr& msg); void parseItemInfo(const InputMessagePtr& msg);
void parsePlayerInventory(const InputMessagePtr& msg); void parsePlayerInventory(const InputMessagePtr& msg);
void parseShowModalDialog(const InputMessagePtr& msg);
void parseExtendedOpcode(const InputMessagePtr& msg); void parseExtendedOpcode(const InputMessagePtr& msg);
public: public:
void setMapDescription(const InputMessagePtr& msg, int x, int y, int z, int width, int height); void setMapDescription(const InputMessagePtr& msg, int x, int y, int z, int width, int height);
int setFloorDescription(const InputMessagePtr& msg, int x, int y, int z, int width, int height, int offset, int skip); int setFloorDescription(const InputMessagePtr& msg, int x, int y, int z, int width, int height, int offset, int skip);
void setTileDescription(const InputMessagePtr& msg, Position position); int setTileDescription(const InputMessagePtr& msg, Position position);
Outfit getOutfit(const InputMessagePtr& msg); Outfit getOutfit(const InputMessagePtr& msg);
ThingPtr getThing(const InputMessagePtr& msg); ThingPtr getThing(const InputMessagePtr& msg);
ThingPtr getMappedThing(const InputMessagePtr& msg);
CreaturePtr getCreature(const InputMessagePtr& msg, int type = 0); CreaturePtr getCreature(const InputMessagePtr& msg, int type = 0);
ItemPtr getItem(const InputMessagePtr& msg, int id = 0); ItemPtr getItem(const InputMessagePtr& msg, int id = 0);
Position getPosition(const InputMessagePtr& msg); Position getPosition(const InputMessagePtr& msg);

View File

@ -35,13 +35,18 @@
void ProtocolGame::parseMessage(const InputMessagePtr& msg) void ProtocolGame::parseMessage(const InputMessagePtr& msg)
{ {
int opcode = 0; int opcode = -1;
int prevOpcode = 0; int prevOpcode = -1;
try { try {
while(!msg->eof()) { while(!msg->eof()) {
opcode = msg->getU8(); opcode = msg->getU8();
if(!m_gameInitialized && opcode >= Proto::GameServerFirstGameOpcode) {
g_game.processGameStart();
m_gameInitialized = true;
}
// try to parse in lua first // try to parse in lua first
int readPos = msg->getReadPos(); int readPos = msg->getReadPos();
if(callLuaField<bool>("onOpcode", opcode, msg)) if(callLuaField<bool>("onOpcode", opcode, msg))
@ -49,9 +54,6 @@ void ProtocolGame::parseMessage(const InputMessagePtr& msg)
else else
msg->setReadPos(readPos); // restore read pos 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");
switch(opcode) { switch(opcode) {
case Proto::GameServerInitGame: case Proto::GameServerInitGame:
parseInitGame(msg); parseInitGame(msg);
@ -70,8 +72,8 @@ void ProtocolGame::parseMessage(const InputMessagePtr& msg)
break; break;
case Proto::GameServerPing: case Proto::GameServerPing:
case Proto::GameServerPingBack: case Proto::GameServerPingBack:
if((opcode == Proto::GameServerPing && g_game.getProtocolVersion() >= 953) || if((opcode == Proto::GameServerPing && g_game.getClientVersion() >= 953) ||
(opcode == Proto::GameServerPingBack && g_game.getProtocolVersion() < 953)) (opcode == Proto::GameServerPingBack && g_game.getClientVersion() < 953))
parsePingBack(msg); parsePingBack(msg);
else else
parsePing(msg); parsePing(msg);
@ -279,13 +281,13 @@ void ProtocolGame::parseMessage(const InputMessagePtr& msg)
break; break;
// PROTOCOL>=870 // PROTOCOL>=870
case Proto::GameServerSpellDelay: case Proto::GameServerSpellDelay:
parseSpellDelay(msg); parseSpellCooldown(msg);
break; break;
case Proto::GameServerSpellGroupDelay: case Proto::GameServerSpellGroupDelay:
parseSpellGroupDelay(msg); parseSpellGroupCooldown(msg);
break; break;
case Proto::GameServerMultiUseDelay: case Proto::GameServerMultiUseDelay:
parseMultiUseDelay(msg); parseMultiUseCooldown(msg);
break; break;
// PROTOCOL>=910 // PROTOCOL>=910
case Proto::GameServerChannelEvent: case Proto::GameServerChannelEvent:
@ -306,7 +308,7 @@ void ProtocolGame::parseMessage(const InputMessagePtr& msg)
parseExtendedOpcode(msg); parseExtendedOpcode(msg);
break; break;
default: default:
stdext::throw_exception("unknown opcode"); stdext::throw_exception(stdext::format("unhandled opcode %d", (int)opcode));
break; break;
} }
prevOpcode = opcode; prevOpcode = opcode;
@ -319,13 +321,13 @@ void ProtocolGame::parseMessage(const InputMessagePtr& msg)
void ProtocolGame::parseInitGame(const InputMessagePtr& msg) void ProtocolGame::parseInitGame(const InputMessagePtr& msg)
{ {
m_gameInitialized = true;
uint playerId = msg->getU32(); uint playerId = msg->getU32();
int serverBeat = msg->getU16(); int serverBeat = msg->getU16();
bool canReportBugs = msg->getU8(); bool canReportBugs = msg->getU8();
m_localPlayer->setId(playerId); m_localPlayer->setId(playerId);
g_game.processGameStart(m_localPlayer, serverBeat, canReportBugs); g_game.setServerBeat(serverBeat);
g_game.setCanReportBugs(canReportBugs);
} }
void ProtocolGame::parseGMActions(const InputMessagePtr& msg) void ProtocolGame::parseGMActions(const InputMessagePtr& msg)
@ -334,9 +336,9 @@ void ProtocolGame::parseGMActions(const InputMessagePtr& msg)
int numViolationReasons; int numViolationReasons;
if(g_game.getProtocolVersion() >= 860) if(g_game.getClientVersion() >= 860)
numViolationReasons = 20; numViolationReasons = 20;
else if(g_game.getProtocolVersion() >= 854) else if(g_game.getClientVersion() >= 854)
numViolationReasons = 19; numViolationReasons = 19;
else else
numViolationReasons = 32; numViolationReasons = 32;
@ -454,7 +456,7 @@ void ProtocolGame::parseTileAddThing(const InputMessagePtr& msg)
Position pos = getPosition(msg); Position pos = getPosition(msg);
int stackPos = -1; int stackPos = -1;
if(g_game.getProtocolVersion() >= 854) if(g_game.getClientVersion() >= 854)
stackPos = msg->getU8(); stackPos = msg->getU8();
ThingPtr thing = getThing(msg); ThingPtr thing = getThing(msg);
@ -463,54 +465,54 @@ void ProtocolGame::parseTileAddThing(const InputMessagePtr& msg)
void ProtocolGame::parseTileTransformThing(const InputMessagePtr& msg) void ProtocolGame::parseTileTransformThing(const InputMessagePtr& msg)
{ {
Position pos = getPosition(msg); ThingPtr thing = getMappedThing(msg);
int stackPos = msg->getU8(); ThingPtr newThing = getThing(msg);
ThingPtr thing = getThing(msg); if(!thing) {
if(thing) { g_logger.traceError("no thing");
if(!g_map.removeThingByPos(pos, stackPos)) return;
g_logger.traceError("could not remove thing");
g_map.addThing(thing, pos, stackPos);
} }
Position pos = thing->getPosition();
int stackpos = thing->getStackpos();
assert(thing->getStackPriority() == newThing->getStackPriority());
if(!g_map.removeThing(thing)) {
g_logger.traceError("unable to remove thing");
return;
}
g_map.addThing(newThing, pos, stackpos);
} }
void ProtocolGame::parseTileRemoveThing(const InputMessagePtr& msg) void ProtocolGame::parseTileRemoveThing(const InputMessagePtr& msg)
{ {
Position pos = getPosition(msg); ThingPtr thing = getMappedThing(msg);
int stackPos = msg->getU8(); if(!thing) {
g_logger.traceError("no thing");
return;
}
if(!g_map.removeThingByPos(pos, stackPos)) g_map.removeThing(thing);
g_logger.traceError("could not remove thing");
} }
void ProtocolGame::parseCreatureMove(const InputMessagePtr& msg) void ProtocolGame::parseCreatureMove(const InputMessagePtr& msg)
{ {
Position oldPos = getPosition(msg); ThingPtr thing = getMappedThing(msg);
int oldStackpos = msg->getU8();
Position newPos = getPosition(msg); Position newPos = getPosition(msg);
ThingPtr thing = g_map.getThing(oldPos, oldStackpos); if(!thing || !thing->isCreature()) {
if(!thing) { g_logger.traceError("no creature found to move");
g_logger.traceError("could not get thing");
return; return;
} }
CreaturePtr creature = thing->asCreature();
if(!creature) {
g_logger.traceError("thing is not a creature");
return;
}
// update map tiles
if(!g_map.removeThing(thing))
g_logger.traceError("could not remove thing");
int stackPos = -2; int stackPos = -2;
// newer protocols stores creatures in reverse order
// older protocols stores creatures in reverse order if(!g_game.getClientVersion() >= 854)
if(!g_game.getProtocolVersion() >= 854)
stackPos = -1; stackPos = -1;
g_map.removeThing(thing);
g_map.addThing(thing, newPos, stackPos); g_map.addThing(thing, newPos, stackPos);
} }
@ -887,7 +889,7 @@ 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.getProtocolVersion() >= 910) if(g_game.getClientVersion() >= 910)
m_localPlayer->setSpeed(msg->getU16()); m_localPlayer->setSpeed(msg->getU16());
if(g_game.getFeature(Otc::GamePlayerRegenerationTime)) if(g_game.getFeature(Otc::GamePlayerRegenerationTime))
@ -918,29 +920,28 @@ void ProtocolGame::parsePlayerState(const InputMessagePtr& msg)
void ProtocolGame::parsePlayerCancelAttack(const InputMessagePtr& msg) void ProtocolGame::parsePlayerCancelAttack(const InputMessagePtr& msg)
{ {
if(g_game.getProtocolVersion() >= 860) uint seq = 0;
msg->getU32(); // unknown if(g_game.getClientVersion() >= 860)
seq = msg->getU32();
g_game.processAttackCancel(); g_game.processAttackCancel(seq);
} }
void ProtocolGame::parseSpellDelay(const InputMessagePtr& msg) void ProtocolGame::parseSpellCooldown(const InputMessagePtr& msg)
{ {
msg->getU16(); // spell id msg->getU16(); // spell id
msg->getU16(); // cooldown msg->getU32(); // cooldown
msg->getU8(); // unknown
} }
void ProtocolGame::parseSpellGroupDelay(const InputMessagePtr& msg) void ProtocolGame::parseSpellGroupCooldown(const InputMessagePtr& msg)
{ {
msg->getU16(); // spell id msg->getU8(); // group id
msg->getU16(); // cooldown msg->getU32(); // cooldown
msg->getU8(); // unknown
} }
void ProtocolGame::parseMultiUseDelay(const InputMessagePtr& msg) void ProtocolGame::parseMultiUseCooldown(const InputMessagePtr& msg)
{ {
//TODO msg->getU32(); // cooldown
} }
void ProtocolGame::parseCreatureSpeak(const InputMessagePtr& msg) void ProtocolGame::parseCreatureSpeak(const InputMessagePtr& msg)
@ -1225,18 +1226,36 @@ void ProtocolGame::parseChannelEvent(const InputMessagePtr& msg)
void ProtocolGame::parseItemInfo(const InputMessagePtr& msg) void ProtocolGame::parseItemInfo(const InputMessagePtr& msg)
{ {
/*int count = msg.getU16() - 1; int size = msg->getU8();
for(int i = 0; i < count; i++) for(int i=0;i<size;++i) {
{ msg->getU16(); // id
int unknown1 = msg->getU8(); msg->getU8(); // subtype
int unknown2 = msg->getU16(); msg->getString(); // description
std::string unknown3 = msg->getString(); }
}*/
} }
void ProtocolGame::parsePlayerInventory(const InputMessagePtr& msg) void ProtocolGame::parsePlayerInventory(const InputMessagePtr& msg)
{ {
//TODO int size = msg->getU16();
for(int i=0;i<size;++i) {
msg->getU16(); // id
msg->getU8(); // subtype
msg->getU16(); // count
}
}
void ProtocolGame::parseShowModalDialog(const InputMessagePtr& msg)
{
msg->getU32(); // id
msg->getString(); // title
msg->getString(); // message
int size = msg->getU8();
for(int i=0;i<size;++i) {
msg->getString(); // button name
msg->getU8(); // button value
}
msg->getU8(); // default escape button
msg->getU8(); // default enter button
} }
void ProtocolGame::parseExtendedOpcode(const InputMessagePtr& msg) void ProtocolGame::parseExtendedOpcode(const InputMessagePtr& msg)
@ -1275,51 +1294,41 @@ int ProtocolGame::setFloorDescription(const InputMessagePtr& msg, int x, int y,
for(int nx = 0; nx < width; nx++) { for(int nx = 0; nx < width; nx++) {
for(int ny = 0; ny < height; ny++) { for(int ny = 0; ny < height; ny++) {
Position tilePos(x + nx + offset, y + ny + offset, z); Position tilePos(x + nx + offset, y + ny + offset, z);
if(skip == 0)
// clean pre stored tiles skip = setTileDescription(msg, tilePos);
g_map.cleanTile(tilePos); else {
g_map.cleanTile(tilePos);
if(skip == 0) {
int tileOpt = msg->peekU16();
if(tileOpt >= 0xFF00)
skip = (msg->getU16() & 0xFF);
else {
setTileDescription(msg, tilePos);
skip = (msg->getU16() & 0xFF);
}
}
else
skip--; skip--;
}
} }
} }
return skip; return skip;
} }
void ProtocolGame::setTileDescription(const InputMessagePtr& msg, Position position) int ProtocolGame::setTileDescription(const InputMessagePtr& msg, Position position)
{ {
if(g_game.getFeature(Otc::GameEnvironmentEffect))
msg->getU16(); // environment effect
g_map.cleanTile(position); g_map.cleanTile(position);
int stackPos = 0; bool gotEffect = 0;
while(true) { for(int stackPos=0;stackPos<256;stackPos++) {
int inspectItemId = msg->peekU16(); if(msg->peekU16() >= 0xff00)
if(inspectItemId >= 0xFF00) { return msg->getU16() & 0xff;
return;
}
else {
if(stackPos >= 10)
g_logger.traceError(stdext::format("too many things, stackpos=%d, pos=%s", stackPos, stdext::to_string(position)));
ThingPtr thing = getThing(msg); if(g_game.getFeature(Otc::GameEnvironmentEffect) && !gotEffect) {
g_map.addThing(thing, position, -2); msg->getU16(); // environment effect
gotEffect = true;
continue;
} }
stackPos++;
if(stackPos > 10)
g_logger.traceError(stdext::format("too many things, pos=%s, stackpos=%d", stdext::to_string(position), stackPos));
ThingPtr thing = getThing(msg);
g_map.addThing(thing, position, -2);
} }
}
return 0;
}
Outfit ProtocolGame::getOutfit(const InputMessagePtr& msg) Outfit ProtocolGame::getOutfit(const InputMessagePtr& msg)
{ {
Outfit outfit; Outfit outfit;
@ -1376,6 +1385,31 @@ ThingPtr ProtocolGame::getThing(const InputMessagePtr& msg)
return thing; return thing;
} }
ThingPtr ProtocolGame::getMappedThing(const InputMessagePtr& msg)
{
ThingPtr thing;
uint16 x = msg->getU16();
if(x != 0xffff) {
Position pos;
pos.x = x;
pos.y = msg->getU16();
pos.z = msg->getU8();
uint8 stackpos = msg->getU8();
assert(stackpos != 255);
thing = g_map.getThing(pos, stackpos);
if(!thing)
g_logger.traceError(stdext::format("no thing at pos:%s, stackpos:%d", stdext::to_string(pos), stackpos));
} else {
uint32 id = msg->getU32();
thing = g_map.getCreatureById(id);
if(!thing)
g_logger.traceError(stdext::format("no creature with id %u", id));
}
return thing;
}
CreaturePtr ProtocolGame::getCreature(const InputMessagePtr& msg, int type) CreaturePtr ProtocolGame::getCreature(const InputMessagePtr& msg, int type)
{ {
if(type == 0) if(type == 0)
@ -1397,7 +1431,7 @@ CreaturePtr ProtocolGame::getCreature(const InputMessagePtr& msg, int type)
uint id = msg->getU32(); uint id = msg->getU32();
int creatureType; int creatureType;
if(g_game.getProtocolVersion() >= 910) if(g_game.getClientVersion() >= 910)
creatureType = msg->getU8(); creatureType = msg->getU8();
else { else {
if(id >= Proto::PlayerStartId && id < Proto::PlayerEndId) if(id >= Proto::PlayerStartId && id < Proto::PlayerEndId)
@ -1416,8 +1450,13 @@ CreaturePtr ProtocolGame::getCreature(const InputMessagePtr& msg, int type)
if(id == m_localPlayer->getId()) if(id == m_localPlayer->getId())
creature = m_localPlayer; creature = m_localPlayer;
else if(creatureType == Proto::CreatureTypePlayer) else if(creatureType == Proto::CreatureTypePlayer) {
creature = PlayerPtr(new Player); // fixes a bug server side bug where GameInit is not sent and local player id is unknown
if(m_localPlayer->getId() == 0 && name == m_localPlayer->getName())
creature = m_localPlayer;
else
creature = PlayerPtr(new Player);
}
else if(creatureType == Proto::CreatureTypeMonster) else if(creatureType == Proto::CreatureTypeMonster)
creature = MonsterPtr(new Monster); creature = MonsterPtr(new Monster);
else if(creatureType == Proto::CreatureTypeNpc) else if(creatureType == Proto::CreatureTypeNpc)
@ -1447,13 +1486,13 @@ CreaturePtr ProtocolGame::getCreature(const InputMessagePtr& msg, int type)
// emblem is sent only when the creature is not known // emblem is sent only when the creature is not known
int emblem = -1; int emblem = -1;
bool passable = false; bool unpass = true;
if(g_game.getFeature(Otc::GameCreatureEmblems) && !known) if(g_game.getFeature(Otc::GameCreatureEmblems) && !known)
emblem = msg->getU8(); emblem = msg->getU8();
if(g_game.getProtocolVersion() >= 854) if(g_game.getClientVersion() >= 854)
passable = (msg->getU8() == 0); unpass = msg->getU8();
if(creature) { if(creature) {
creature->setHealthPercent(healthPercent); creature->setHealthPercent(healthPercent);
@ -1465,7 +1504,7 @@ CreaturePtr ProtocolGame::getCreature(const InputMessagePtr& msg, int type)
creature->setShield(shield); creature->setShield(shield);
if(emblem != -1) if(emblem != -1)
creature->setEmblem(emblem); creature->setEmblem(emblem);
creature->setPassable(passable); creature->setPassable(!unpass);
if(creature == m_localPlayer && !m_localPlayer->isKnown()) if(creature == m_localPlayer && !m_localPlayer->isKnown())
m_localPlayer->setKnown(true); m_localPlayer->setKnown(true);
@ -1481,11 +1520,11 @@ CreaturePtr ProtocolGame::getCreature(const InputMessagePtr& msg, int type)
if(creature) if(creature)
creature->turn(direction); creature->turn(direction);
if(g_game.getProtocolVersion() >= 953) { if(g_game.getClientVersion() >= 953) {
bool passable = msg->getU8(); bool unpass = msg->getU8();
if(creature) if(creature)
creature->setPassable(passable); creature->setPassable(!unpass);
} }
} else { } else {
@ -1502,7 +1541,7 @@ ItemPtr ProtocolGame::getItem(const InputMessagePtr& msg, int id)
ItemPtr item = Item::create(id); ItemPtr item = Item::create(id);
if(item->getId() == 0) if(item->getId() == 0)
stdext::throw_exception("unable to create item with invalid id 0"); stdext::throw_exception(stdext::format("unable to create item with invalid id %d", id));
if(item->isStackable() || item->isFluidContainer() || item->isSplash() || item->isChargeable()) if(item->isStackable() || item->isFluidContainer() || item->isSplash() || item->isChargeable())
item->setCountOrSubType(msg->getU8()); item->setCountOrSubType(msg->getU8());
@ -1511,7 +1550,7 @@ ItemPtr ProtocolGame::getItem(const InputMessagePtr& msg, int id)
if(item->getAnimationPhases() > 1) { if(item->getAnimationPhases() > 1) {
// 0xfe => random phase // 0xfe => random phase
// 0xff => async? // 0xff => async?
msg->getU8(); msg->getU8(); // phase
} }
} }

View File

@ -51,7 +51,7 @@ void ProtocolGame::sendLoginPacket(uint challangeTimestamp, uint8 challangeRando
msg->addU8(Proto::ClientEnterGame); msg->addU8(Proto::ClientEnterGame);
msg->addU16(g_lua.callGlobalField<int>("g_game", "getOs")); msg->addU16(g_lua.callGlobalField<int>("g_game", "getOs"));
msg->addU16(g_game.getProtocolVersion()); msg->addU16(g_game.getClientVersion());
int paddingBytes = 128; int paddingBytes = 128;
msg->addU8(0); // first RSA byte must be 0 msg->addU8(0); // first RSA byte must be 0
@ -509,21 +509,21 @@ void ProtocolGame::sendChangeFightModes(Otc::FightModes fightMode, Otc::ChaseMod
send(msg); send(msg);
} }
void ProtocolGame::sendAttack(uint creatureId) void ProtocolGame::sendAttack(uint creatureId, uint seq)
{ {
OutputMessagePtr msg(new OutputMessage); OutputMessagePtr msg(new OutputMessage);
msg->addU8(Proto::ClientAttack); msg->addU8(Proto::ClientAttack);
msg->addU32(creatureId); msg->addU32(creatureId);
msg->addU32(0); msg->addU32(seq);
msg->addU32(0);
send(msg); send(msg);
} }
void ProtocolGame::sendFollow(uint creatureId) void ProtocolGame::sendFollow(uint creatureId, uint seq)
{ {
OutputMessagePtr msg(new OutputMessage); OutputMessagePtr msg(new OutputMessage);
msg->addU8(Proto::ClientFollow); msg->addU8(Proto::ClientFollow);
msg->addU32(creatureId); msg->addU32(creatureId);
msg->addU32(seq);
send(msg); send(msg);
} }
@ -566,14 +566,14 @@ void ProtocolGame::sendLeaveParty()
send(msg); send(msg);
} }
void ProtocolGame::sendShareExperience(bool active, int unknown) void ProtocolGame::sendShareExperience(bool active)
{ {
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(g_game.getProtocolVersion() < 910) if(g_game.getClientVersion() < 910)
msg->addU8(unknown); msg->addU8(0);
send(msg); send(msg);
} }

View File

@ -34,18 +34,18 @@ SpriteManager::SpriteManager()
m_signature = 0; m_signature = 0;
} }
void SpriteManager::termiante() void SpriteManager::terminate()
{ {
unload(); unload();
} }
bool SpriteManager::loadSpr(const std::string& file) bool SpriteManager::loadSpr(const std::string& file)
{ {
m_spritesCount = 0;
m_signature = 0;
m_loaded = false;
try { try {
m_spritesFile = g_resources.openFile(file); m_spritesFile = g_resources.openFile(file);
if(!m_spritesFile)
return false;
// cache file buffer to avoid lags from hard drive // cache file buffer to avoid lags from hard drive
m_spritesFile->cache(); m_spritesFile->cache();

View File

@ -32,7 +32,7 @@ class SpriteManager
public: public:
SpriteManager(); SpriteManager();
void termiante(); void terminate();
bool loadSpr(const std::string& file); bool loadSpr(const std::string& file);
void unload(); void unload();

View File

@ -66,7 +66,7 @@ ContainerPtr Thing::getParentContainer()
int Thing::getStackpos() int Thing::getStackpos()
{ {
if(m_position.x == 65535 && asItem()) // is inside a container if(m_position.x == 65535 && asItem()) // is inside a container
return 0; return m_position.z;
else if(const TilePtr& tile = getTile()) else if(const TilePtr& tile = getTile())
return tile->getThingStackpos(asThing()); return tile->getThingStackpos(asThing());
else { else {

View File

@ -59,16 +59,17 @@ void ThingTypeManager::terminate()
bool ThingTypeManager::loadDat(const std::string& file) bool ThingTypeManager::loadDat(const std::string& file)
{ {
m_datLoaded = false;
m_datSignature = 0;
try { try {
FileStreamPtr fin = g_resources.openFile(file); FileStreamPtr fin = g_resources.openFile(file);
if(!fin)
stdext::throw_exception("unable to open file");
m_datSignature = fin->getU32(); m_datSignature = fin->getU32();
int numThings[ThingLastCategory]; int numThings[ThingLastCategory];
for(int category = 0; category < ThingLastCategory; ++category) { for(int category = 0; category < ThingLastCategory; ++category) {
int count = fin->getU16() + 1; int count = fin->getU16() + 1;
m_thingTypes[category].clear();
m_thingTypes[category].resize(count, m_nullThingType); m_thingTypes[category].resize(count, m_nullThingType);
} }
@ -114,9 +115,9 @@ void ThingTypeManager::loadOtb(const std::string& file)
m_itemTypes.resize(root->getChildren().size(), m_nullItemType); m_itemTypes.resize(root->getChildren().size(), m_nullItemType);
for(const BinaryTreePtr& node : root->getChildren()) { for(const BinaryTreePtr& node : root->getChildren()) {
ItemTypePtr otbType(new ItemType); ItemTypePtr itemType(new ItemType);
otbType->unserialize(node); itemType->unserialize(node);
addItemType(otbType); addItemType(itemType);
} }
m_otbLoaded = true; m_otbLoaded = true;
@ -171,25 +172,25 @@ void ThingTypeManager::parseItemType(uint16 id, TiXmlElement* elem)
addItemType(newType); addItemType(newType);
} }
ItemTypePtr otbType = getItemType(serverId); ItemTypePtr itemType = getItemType(serverId);
otbType->setName(elem->Attribute("name")); itemType->setName(elem->Attribute("name"));
for(TiXmlElement* attrib = elem->FirstChildElement(); attrib; attrib = attrib->NextSiblingElement()) { for(TiXmlElement* attrib = elem->FirstChildElement(); attrib; attrib = attrib->NextSiblingElement()) {
if(attrib->ValueStr() != "attribute") if(attrib->ValueStr() != "attribute")
break; break;
if(attrib->Attribute("key") == "description") { if(attrib->Attribute("key") == "description") {
otbType->setDesc(attrib->Attribute("value")); itemType->setDesc(attrib->Attribute("value"));
break; break;
} }
} }
} }
void ThingTypeManager::addItemType(const ItemTypePtr& otbType) void ThingTypeManager::addItemType(const ItemTypePtr& itemType)
{ {
uint16 id = otbType->getServerId(); uint16 id = itemType->getServerId();
if(m_itemTypes.size() <= id) if(m_itemTypes.size() <= id)
m_itemTypes.resize(id+1, m_nullItemType); m_itemTypes.resize(id+1, m_nullItemType);
m_itemTypes[id] = otbType; m_itemTypes[id] = itemType;
} }
const ItemTypePtr& ThingTypeManager::findOtbForClientId(uint16 id) const ItemTypePtr& ThingTypeManager::findOtbForClientId(uint16 id)
@ -197,9 +198,9 @@ const ItemTypePtr& ThingTypeManager::findOtbForClientId(uint16 id)
if(m_itemTypes.empty()) if(m_itemTypes.empty())
return m_nullItemType; return m_nullItemType;
for(const ItemTypePtr& otbType : m_itemTypes) { for(const ItemTypePtr& itemType : m_itemTypes) {
if(otbType->getClientId() == id) if(itemType->getClientId() == id)
return otbType; return itemType;
} }
return m_nullItemType; return m_nullItemType;

View File

@ -40,7 +40,7 @@ public:
void loadXml(const std::string& file); void loadXml(const std::string& file);
void parseItemType(uint16 id, TiXmlElement *elem); void parseItemType(uint16 id, TiXmlElement *elem);
void addItemType(const ItemTypePtr& otbType); void addItemType(const ItemTypePtr& itemType);
const ItemTypePtr& findOtbForClientId(uint16 id); const ItemTypePtr& findOtbForClientId(uint16 id);
const ThingTypePtr& getNullThingType() { return m_nullThingType; } const ThingTypePtr& getNullThingType() { return m_nullThingType; }

View File

@ -392,7 +392,7 @@ bool Tile::isWalkable()
return false; return false;
if(CreaturePtr creature = thing->asCreature()) { if(CreaturePtr creature = thing->asCreature()) {
if(!creature->getPassable()) if(!creature->isPassable())
return false; return false;
} }
} }
@ -405,7 +405,7 @@ bool Tile::isPathable()
return false; return false;
for(const ThingPtr& thing : m_things) { for(const ThingPtr& thing : m_things) {
if(thing->isNotPathable()) if(thing->isNotPathable() || thing->isCreature())
return false; return false;
} }
@ -507,4 +507,13 @@ void Tile::update()
if(c != 0) if(c != 0)
m_minimapColorByte = c; m_minimapColorByte = c;
} }
// check stack priorities
// this code exists to find stackpos bugs faster
int lastPriority = 0;
for(const ThingPtr& thing : m_things) {
int priority = thing->getStackPriority();
assert(lastPriority <= priority);
lastPriority = priority;
}
} }