More multiprotocol support

master
Eduardo Bart 12 lat temu
rodzic e393bc245d
commit c795eb91ab

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

@ -168,7 +168,8 @@ function EnterGame.doLogin()
end })
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)
end

@ -72,7 +72,14 @@ function connect(object, arg1, arg2, arg3)
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
if not object[signal] then
elseif type(object[signal]) == 'function' then
@ -231,11 +238,19 @@ end
function signalcall(param, ...)
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
for k,v in pairs(param) do
if v(...) then
return true
local status, ret = pcall(v, ...)
if status then
if ret then return true end
else
perror(ret)
end
end
elseif func ~= nil then

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

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

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

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

@ -2,7 +2,7 @@ Icons = {}
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[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[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' }

@ -238,6 +238,7 @@ function startTradeWith(thing)
end
function createThingMenu(menuPosition, lookThing, useThing, creatureThing)
if not g_game.isOnline() then return end
local menu = g_ui.createWidget('PopupMenu')
if lookThing then
@ -297,52 +298,50 @@ function createThingMenu(menuPosition, lookThing, useThing, creatureThing)
else
local localPlayer = g_game.getLocalPlayer()
if localPlayer then
if g_game.getAttackingCreature() ~= creatureThing then
menu:addOption(tr('Attack'), function() g_game.attack(creatureThing) end)
else
menu:addOption(tr('Stop Attack'), function() g_game.cancelAttack() end)
end
if g_game.getAttackingCreature() ~= creatureThing then
menu:addOption(tr('Attack'), function() g_game.attack(creatureThing) end)
else
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)
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
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
local localPlayerShield = localPlayer:asCreature():getShield()
local creatureShield = creatureThing:getShield()
if localPlayerShield == ShieldNone or localPlayerShield == ShieldWhiteBlue then
if creatureShield == ShieldWhiteYellow then
menu:addOption(tr('Join %s\'s Party', creatureThing:getName()), function() g_game.partyJoin(creatureThing:getId()) end)
else
menu:addOption(tr('Invite to Party'), function() g_game.partyInvite(creatureThing:getId()) end)
end
if (not Player:hasVip(creatureName)) then
menu:addOption(tr('Add to VIP list'), function() g_game.addVip(creatureName) 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
local localPlayerShield = localPlayer:asCreature():getShield()
local creatureShield = creatureThing:getShield()
if localPlayerShield == ShieldNone or localPlayerShield == ShieldWhiteBlue then
if creatureShield == ShieldWhiteYellow then
menu:addOption(tr('Join %s\'s Party', creatureThing:getName()), function() g_game.partyJoin(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
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

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

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

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

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

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

@ -1,11 +1,17 @@
function init()
if not g_things.loadDat('/game_tibiafiles/Tibia.dat') then
fatal(tr("Unable to load dat file, please place a valid Tibia dat in modules/game_tibiafiles/Tibia.dat"))
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"))
if g_game.getClientVersion() ~= 0 then
load()
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

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

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

@ -1,21 +1,37 @@
-- @docclass
ProtocolLogin = extends(Protocol)
-- set to the latest Tibia.pic signature to make otclient compatible with official tibia
local PIC_SIGNATURE = 1337606793
LoginServerError = 10
LoginServerMotd = 20
LoginServerUpdateNeeded = 30
LoginServerCharacterList = 100
function ProtocolLogin:login(host, port, accountName, accountPassword)
if string.len(accountName) == 0 or string.len(accountPassword) == 0 then
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
self.accountName = accountName
self.accountPassword = accountPassword
self.connectCallback = sendLoginPacket
self:connect(host, port)
end
-- private functions
local function sendLoginPacket(protocol)
function ProtocolLogin:cancelLogin()
self:disconnect()
end
function ProtocolLogin:sendLoginPacket()
local msg = OutputMessage.create()
msg:addU8(ClientOpcodes.ClientEnterAccount)
msg:addU16(g_game.getOsType())
msg:addU16(g_game.getProtocolVersion())
msg:addU16(g_game.getClientVersion())
msg:addU32(g_things.getDatSignature())
msg:addU32(g_sprites.getSprSignature())
@ -26,8 +42,8 @@ local function sendLoginPacket(protocol)
paddingBytes = paddingBytes - 1
-- xtea key
protocol:generateXteaKey()
local xteaKey = protocol:getXteaKey()
self:generateXteaKey()
local xteaKey = self:getXteaKey()
msg:addU32(xteaKey[1])
msg:addU32(xteaKey[2])
msg:addU32(xteaKey[3])
@ -35,30 +51,29 @@ local function sendLoginPacket(protocol)
paddingBytes = paddingBytes - 16
if g_game.getFeature(GameProtocolChecksum) then
protocol:enableChecksum()
self: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))
msg:addString(self.accountName)
msg:addString(self.accountPassword)
paddingBytes = paddingBytes - (4 + string.len(self.accountName) + string.len(self.accountPassword))
else
msg:addU32(tonumber(protocol.accountName))
msg:addString(protocol.accountPassword)
paddingBytes = paddingBytes - (6 + string.len(protocol.accountPassword))
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())
protocol:send(msg)
protocol:enableXteaEncryption()
protocol:recv()
self:send(msg)
self:enableXteaEncryption()
self:recv()
end
-- events
function ProtocolLogin:onConnect()
self:connectCallback(self)
self:sendLoginPacket()
end
function ProtocolLogin:onRecv(msg)
@ -79,32 +94,6 @@ function ProtocolLogin:onRecv(msg)
self:disconnect()
end
-- public functions
function ProtocolLogin.create()
return ProtocolLogin.internalCreate()
end
function ProtocolLogin:login(host, port, accountName, accountPassword)
if string.len(accountName) == 0 or string.len(accountPassword) == 0 then
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
self.accountName = accountName
self.accountPassword = accountPassword
self.connectCallback = sendLoginPacket
self:connect(host, port)
end
function ProtocolLogin:cancelLogin()
self:disconnect()
end
function ProtocolLogin:parseError(msg)
local errorMessage = msg:getString()
signalcall(self.onError, self, errorMessage)

@ -37,14 +37,14 @@ static const std::string glslMainWithTexCoordsVertexShader = "\n\
void main()\n\
{\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";
static std::string glslPositionOnlyVertexShader = "\n\
attribute highp vec2 a_Vertex;\n\
uniform highp mat3 u_ProjectionMatrix;\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";
static const std::string glslMainFragmentShader = "\n\

@ -336,6 +336,7 @@ void Application::registerLuaFunctions()
g_lua.bindClassMemberFunction<UIWidget>("getChildByIndex", &UIWidget::getChildByIndex);
g_lua.bindClassMemberFunction<UIWidget>("recursiveGetChildById", &UIWidget::recursiveGetChildById);
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>("recursiveGetChildrenByMarginPos", &UIWidget::recursiveGetChildrenByMarginPos);
g_lua.bindClassMemberFunction<UIWidget>("backwardsGetWidgetById", &UIWidget::backwardsGetWidgetById);

@ -56,7 +56,7 @@ private:
SoundSourcePtr createSoundSource(const std::string& filename);
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;
SoundSourcePtr m_musicSource;
ALCdevice *m_device;

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

@ -1130,6 +1130,18 @@ UIWidgetPtr UIWidget::recursiveGetChildByPos(const Point& childPos, bool wantsPh
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 children;

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

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

@ -39,15 +39,16 @@ Game::Game()
{
resetGameStates();
m_protocolVersion = 0;
setProtocolVersion(PROTOCOL);
}
void Game::resetGameStates()
{
m_online = false;
m_denyBotCall = false;
m_dead = false;
m_mounted = false;
m_serverBeat = 50;
m_seq = 0;
m_canReportBugs = false;
m_fightMode = Otc::FightBalanced;
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);
}
void Game::processGameStart(const LocalPlayerPtr& localPlayer, int serverBeat, bool canReportBugs)
void Game::processGameStart()
{
// reset the new game state
resetGameStates();
m_localPlayer = localPlayer;
m_localPlayer->setName(m_characterName);
m_serverBeat = serverBeat;
m_canReportBugs = canReportBugs;
m_online = true;
// synchronize fight modes with the server
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);
}
void Game::processAttackCancel()
void Game::processAttackCancel(uint seq)
{
if(isAttacking())
if(isAttacking() && (seq == 0 || m_seq == seq))
setAttackingCreature(nullptr);
}
@ -423,6 +418,12 @@ void Game::loginWorld(const std::string& account, const std::string& password, c
if(m_protocolVersion == 0)
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->login(account, password, worldHost, (uint16)worldPort, characterName);
m_characterName = characterName;
@ -505,15 +506,10 @@ void Game::autoWalk(const std::vector<Otc::Direction>& dirs)
Otc::Direction direction = dirs.front();
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);
forceWalk(direction);
std::vector<Otc::Direction> nextDirs = dirs;
nextDirs.erase(nextDirs.begin());
if(nextDirs.size() > 0)
m_protocolGame->sendAutoWalk(nextDirs);
m_protocolGame->sendAutoWalk(dirs);
g_lua.callGlobalField("g_game", "onAutoWalk", direction);
}
@ -737,7 +733,7 @@ void Game::attack(const CreaturePtr& creature)
cancelFollow();
setAttackingCreature(creature);
m_protocolGame->sendAttack(creature ? creature->getId() : 0);
m_protocolGame->sendAttack(creature ? creature->getId() : 0, ++m_seq);
}
void Game::follow(const CreaturePtr& creature)
@ -751,7 +747,7 @@ void Game::follow(const CreaturePtr& creature)
cancelAttack();
setFollowingCreature(creature);
m_protocolGame->sendFollow(creature ? creature->getId() : 0);
m_protocolGame->sendFollow(creature ? creature->getId() : 0, ++m_seq);
}
void Game::cancelAttackAndFollow()
@ -885,7 +881,7 @@ void Game::partyShareExperience(bool active)
{
if(!canPerformGameAction())
return;
m_protocolGame->sendShareExperience(active, 0);
m_protocolGame->sendShareExperience(active);
}
void Game::requestOutfit()
@ -1084,15 +1080,16 @@ bool Game::checkBotProtection()
bool Game::canPerformGameAction()
{
// we can only perform game actions if we meet these conditions:
// - the game is online
// - the local player exists
// - the local player is not dead
// - we have a game protocol
// - the game protocol is connected
// - 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())
stdext::throw_exception("Unable to change client version while online");
@ -1147,6 +1144,8 @@ void Game::setProtocolVersion(int version)
}
m_protocolVersion = version;
g_lua.callGlobalField("g_game", "onClientVersionChange", version);
}
void Game::setAttackingCreature(const CreaturePtr& creature)

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

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

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

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

@ -175,7 +175,7 @@ public:
private:
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::vector<AnimatedTextPtr> m_animatedTexts;
std::vector<StaticTextPtr> m_staticTexts;

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

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

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

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

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

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

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

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

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

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

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

@ -392,7 +392,7 @@ bool Tile::isWalkable()
return false;
if(CreaturePtr creature = thing->asCreature()) {
if(!creature->getPassable())
if(!creature->isPassable())
return false;
}
}
@ -405,7 +405,7 @@ bool Tile::isPathable()
return false;
for(const ThingPtr& thing : m_things) {
if(thing->isNotPathable())
if(thing->isNotPathable() || thing->isCreature())
return false;
}
@ -507,4 +507,13 @@ void Tile::update()
if(c != 0)
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;
}
}

Ładowanie…
Anuluj
Zapisz