diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/CMakeLists.txt | 6 | ||||
-rw-r--r-- | src/Makefile.am | 6 | ||||
-rw-r--r-- | src/net/ea/beinghandler.cpp | 425 | ||||
-rw-r--r-- | src/net/ea/beinghandler.h | 34 | ||||
-rw-r--r-- | src/net/ea/beingnet.cpp | 473 | ||||
-rw-r--r-- | src/net/ea/beingnet.h | 59 | ||||
-rw-r--r-- | src/net/eathena/beinghandler.cpp | 1727 | ||||
-rw-r--r-- | src/net/eathena/beinghandler.h | 119 | ||||
-rw-r--r-- | src/net/eathena/beingnet.cpp | 1670 | ||||
-rw-r--r-- | src/net/eathena/beingnet.h | 101 | ||||
-rw-r--r-- | src/net/tmwa/beinghandler.cpp | 1388 | ||||
-rw-r--r-- | src/net/tmwa/beinghandler.h | 51 | ||||
-rw-r--r-- | src/net/tmwa/beingnet.cpp | 1391 | ||||
-rw-r--r-- | src/net/tmwa/beingnet.h | 66 |
14 files changed, 3874 insertions, 3642 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 176c01c65..8a2e4879c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1343,6 +1343,8 @@ SET(SRCS_EVOL net/ea/admin.h net/ea/adminhandler.cpp net/ea/adminhandler.h + net/ea/beingnet.cpp + net/ea/beingnet.h net/ea/beinghandler.cpp net/ea/beinghandler.h net/ea/buysellhandler.cpp @@ -1385,6 +1387,8 @@ SET(SRCS_TMWA gui/windows/shopselldialog.h net/tmwa/adminhandler.cpp net/tmwa/adminhandler.h + net/tmwa/beingnet.cpp + net/tmwa/beingnet.h net/tmwa/beinghandler.cpp net/tmwa/beinghandler.h net/tmwa/buysellhandler.cpp @@ -1532,6 +1536,8 @@ SET(SRCS_EATHENA net/eathena/battlegroundhandler.h net/eathena/cashshophandler.cpp net/eathena/cashshophandler.h + net/eathena/beingnet.cpp + net/eathena/beingnet.h net/eathena/beinghandler.cpp net/eathena/beinghandler.h net/eathena/buyingstorehandler.cpp diff --git a/src/Makefile.am b/src/Makefile.am index 381e6ab69..2ce4d5b80 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1185,6 +1185,8 @@ manaplus_SOURCES += main.cpp \ net/ea/admin.h \ net/ea/adminhandler.cpp \ net/ea/adminhandler.h \ + net/ea/beingnet.cpp \ + net/ea/beingnet.h \ net/ea/beinghandler.cpp \ net/ea/beinghandler.h \ net/ea/buysellhandler.cpp \ @@ -1228,6 +1230,8 @@ manaplus_SOURCES += \ gui/windows/shopselldialog.h \ net/tmwa/adminhandler.cpp \ net/tmwa/adminhandler.h \ + net/tmwa/beingnet.cpp \ + net/tmwa/beingnet.h \ net/tmwa/beinghandler.cpp \ net/tmwa/beinghandler.h \ net/tmwa/buysellhandler.cpp \ @@ -1378,6 +1382,8 @@ manaplus_SOURCES += gui/windows/bankwindow.cpp \ net/eathena/battlegroundhandler.h \ net/eathena/cashshophandler.cpp \ net/eathena/cashshophandler.h \ + net/eathena/beingnet.cpp \ + net/eathena/beingnet.h \ net/eathena/beinghandler.cpp \ net/eathena/beinghandler.h \ net/eathena/buyingstorehandler.cpp \ diff --git a/src/net/ea/beinghandler.cpp b/src/net/ea/beinghandler.cpp index 85e04efbc..49e8b4720 100644 --- a/src/net/ea/beinghandler.cpp +++ b/src/net/ea/beinghandler.cpp @@ -40,434 +40,17 @@ #include "net/serverfeatures.h" +#include "net/ea/beingnet.h" + #include "debug.h" namespace Ea { -BeingId BeingHandler::mSpawnId = BeingId_zero; -bool BeingHandler::mSync = false; BeingHandler::BeingHandler(const bool enableSync) { - mSync = enableSync; - mSpawnId = BeingId_zero; -} - -Being *BeingHandler::createBeing(const BeingId id, - const int job) -{ - if (!actorManager) - return nullptr; - - ActorTypeT type = ActorType::Unknown; - if (job <= 25 || (job >= 4001 && job <= 4049)) - type = ActorType::Player; - else if (job >= 46 && job <= 1000) - type = ActorType::Npc; - else if (job > 1000 && job <= 2000) - type = ActorType::Monster; - else if (job == 45) - type = ActorType::Portal; - - Being *const being = actorManager->createBeing( - id, type, fromInt(job, BeingTypeId)); - return being; -} - -void BeingHandler::setSprite(Being *const being, const unsigned int slot, - const int id, const std::string &color, - const ItemColor colorId, - const bool isWeapon, - const bool isTempSprite) -{ - if (!being) - return; - being->updateSprite(slot, id, color, colorId, isWeapon, isTempSprite); -} - -void BeingHandler::processBeingRemove(Net::MessageIn &msg) -{ - BLOCK_START("BeingHandler::processBeingRemove") - if (!actorManager || !localPlayer) - { - BLOCK_END("BeingHandler::processBeingRemove") - return; - } - - // A being should be removed or has died - - const BeingId id = msg.readBeingId("being id"); - Being *const dstBeing = actorManager->findBeing(id); - if (!dstBeing) - { - msg.readUInt8("dead flag?"); - BLOCK_END("BeingHandler::processBeingRemove") - return; - } - - localPlayer->followMoveTo(dstBeing, localPlayer->getNextDestX(), - localPlayer->getNextDestY()); - - // If this is player's current target, clear it. - if (dstBeing == localPlayer->getTarget()) - localPlayer->stopAttack(true); - - if (msg.readUInt8("dead flag?") == 1U) - { - if (dstBeing->getCurrentAction() != BeingAction::DEAD) - { - dstBeing->setAction(BeingAction::DEAD, 0); - dstBeing->recalcSpritesOrder(); - } - } - else - { - if (dstBeing->getType() == ActorType::Player) - { - if (socialWindow) - socialWindow->updateActiveList(); - } - actorManager->destroy(dstBeing); - } - BLOCK_END("BeingHandler::processBeingRemove") -} - -void BeingHandler::processSkillDamage(Net::MessageIn &msg) -{ - BLOCK_START("BeingHandler::processSkillDamage") - if (!actorManager) - { - BLOCK_END("BeingHandler::processSkillDamage") - return; - } - - const int id = msg.readInt16("skill id"); - Being *const srcBeing = actorManager->findBeing( - msg.readBeingId("src being id")); - Being *const dstBeing = actorManager->findBeing( - msg.readBeingId("dst being id")); - msg.readInt32("tick"); - msg.readInt32("src speed"); - msg.readInt32("dst speed"); - const int param1 = msg.readInt32("damage"); - const int level = msg.readInt16("skill level"); - msg.readInt16("div"); - msg.readUInt8("skill hit/type?"); - if (srcBeing) - srcBeing->handleSkill(dstBeing, param1, id, level); - if (dstBeing) - dstBeing->takeDamage(srcBeing, param1, AttackType::SKILL, id, level); - BLOCK_END("BeingHandler::processSkillDamage") -} - -void BeingHandler::processBeingAction(Net::MessageIn &msg) -{ - BLOCK_START("BeingHandler::processBeingAction") - if (!actorManager) - { - BLOCK_END("BeingHandler::processBeingAction") - return; - } - - Being *const srcBeing = actorManager->findBeing( - msg.readBeingId("src being id")); - Being *const dstBeing = actorManager->findBeing( - msg.readBeingId("dst being id")); - - msg.readInt32("tick"); - const int srcSpeed = msg.readInt32("src speed"); - msg.readInt32("dst speed"); - const int param1 = msg.readInt16("param1"); - msg.readInt16("param 2"); - const AttackTypeT type = static_cast<AttackTypeT>( - msg.readUInt8("type")); - msg.readInt16("param 3"); - - switch (type) - { - case AttackType::HIT: // Damage - case AttackType::CRITICAL: // Critical Damage - case AttackType::MULTI: // Critical Damage - case AttackType::REFLECT: // Reflected Damage - case AttackType::FLEE: // Lucky Dodge - if (srcBeing) - { - if (srcSpeed && srcBeing->getType() == ActorType::Player) - srcBeing->setAttackDelay(srcSpeed); - // attackid=1, type - srcBeing->handleAttack(dstBeing, param1, 1); - if (srcBeing->getType() == ActorType::Player) - srcBeing->setAttackTime(); - } - if (dstBeing) - { - // level not present, using 1 - dstBeing->takeDamage(srcBeing, param1, - static_cast<AttackTypeT>(type), 1); - } - break; - - case AttackType::PICKUP: - break; - // tmw server can send here garbage? -// if (srcBeing) -// srcBeing->setAction(BeingAction::DEAD, 0); - - case AttackType::SIT: - if (srcBeing) - { - srcBeing->setAction(BeingAction::SIT, 0); - if (srcBeing->getType() == ActorType::Player) - { - srcBeing->setMoveTime(); - if (localPlayer) - localPlayer->imitateAction(srcBeing, BeingAction::SIT); - } - } - break; - - case AttackType::STAND: - if (srcBeing) - { - srcBeing->setAction(BeingAction::STAND, 0); - if (srcBeing->getType() == ActorType::Player) - { - srcBeing->setMoveTime(); - if (localPlayer) - { - localPlayer->imitateAction(srcBeing, - BeingAction::STAND); - } - } - } - break; - default: - case AttackType::SPLASH: - case AttackType::SKILL: - case AttackType::REPEATE: - case AttackType::MULTI_REFLECT: - case AttackType::TOUCH_SKILL: - case AttackType::MISS: - case AttackType::SKILLMISS: - UNIMPLIMENTEDPACKET; - break; - } - BLOCK_END("BeingHandler::processBeingAction") -} - -void BeingHandler::processBeingEmotion(Net::MessageIn &msg) -{ - BLOCK_START("BeingHandler::processBeingEmotion") - if (!localPlayer || !actorManager) - { - BLOCK_END("BeingHandler::processBeingEmotion") - return; - } - - Being *const dstBeing = actorManager->findBeing( - msg.readBeingId("being id")); - if (!dstBeing) - { - BLOCK_END("BeingHandler::processBeingEmotion") - return; - } - - if (player_relations.hasPermission(dstBeing, PlayerRelation::EMOTE)) - { - const uint8_t emote = msg.readUInt8("emote"); - if (emote) - { - dstBeing->setEmote(emote, 0); - localPlayer->imitateEmote(dstBeing, emote); - } - } - if (dstBeing->getType() == ActorType::Player) - dstBeing->setOtherTime(); - BLOCK_END("BeingHandler::processBeingEmotion") -} - -void BeingHandler::processNameResponse(Net::MessageIn &msg) -{ - BLOCK_START("BeingHandler::processNameResponse") - if (!localPlayer || !actorManager) - { - BLOCK_END("BeingHandler::processNameResponse") - return; - } - - const BeingId beingId = msg.readBeingId("being id"); - Being *const dstBeing = actorManager->findBeing(beingId); - - if (dstBeing) - { - if (beingId == localPlayer->getId()) - { - localPlayer->pingResponse(); - } - else - { - const std::string name = msg.readString(24, "name"); - if (dstBeing->getType() != ActorType::Portal) - { - dstBeing->setName(name); - } - else if (viewport) - { - Map *const map = viewport->getMap(); - if (map) - { - map->addPortalTile(name, MapItemType::PORTAL, - dstBeing->getTileX(), dstBeing->getTileY()); - } - } - dstBeing->updateGuild(); - dstBeing->addToCache(); - - if (dstBeing->getType() == ActorType::Player) - dstBeing->updateColors(); - - if (localPlayer) - { - const Party *const party = localPlayer->getParty(); - if (party && party->isMember(dstBeing->getId())) - { - PartyMember *const member = party->getMember( - dstBeing->getId()); - - if (member) - member->setName(dstBeing->getName()); - } - localPlayer->checkNewName(dstBeing); - } - BLOCK_END("BeingHandler::processNameResponse") - return; - } - } - msg.readString(24, "name"); - BLOCK_END("BeingHandler::processNameResponse") -} - -void BeingHandler::processPlayerStop(Net::MessageIn &msg) -{ - BLOCK_START("BeingHandler::processPlayerStop") - if (!actorManager || !localPlayer) - { - BLOCK_END("BeingHandler::processPlayerStop") - return; - } - - const BeingId id = msg.readBeingId("account id"); - - if (mSync || id != localPlayer->getId()) - { - Being *const dstBeing = actorManager->findBeing(id); - if (dstBeing) - { - const uint16_t x = msg.readInt16("x"); - const uint16_t y = msg.readInt16("y"); - dstBeing->setTileCoords(x, y); - if (dstBeing->getCurrentAction() == BeingAction::MOVE) - dstBeing->setAction(BeingAction::STAND, 0); - BLOCK_END("BeingHandler::processPlayerStop") - return; - } - } - msg.readInt16("x"); - msg.readInt16("y"); - BLOCK_END("BeingHandler::processPlayerStop") -} - -void BeingHandler::processPlayerMoveToAttack(Net::MessageIn &msg) -{ - BLOCK_START("BeingHandler::processPlayerStop") - msg.readInt32("target id"); - msg.readInt16("target x"); - msg.readInt16("target y"); - msg.readInt16("x"); - msg.readInt16("y"); - msg.readInt16("attack range"); - - if (localPlayer) - localPlayer->fixAttackTarget(); - BLOCK_END("BeingHandler::processPlayerStop") -} - -void BeingHandler::processSkillNoDamage(Net::MessageIn &msg) -{ - const int id = msg.readInt16("skill id"); - const int heal = msg.readInt16("heal"); - Being *const dstBeing = actorManager->findBeing( - msg.readBeingId("dst being id")); - Being *const srcBeing = actorManager->findBeing( - msg.readBeingId("src being id")); - const int flag = msg.readUInt8("fail"); - - if (srcBeing) - srcBeing->handleSkill(dstBeing, heal, id, 1); -} - -void BeingHandler::processPvpMapMode(Net::MessageIn &msg) -{ - BLOCK_START("BeingHandler::processPvpMapMode") - const Game *const game = Game::instance(); - if (!game) - { - BLOCK_END("BeingHandler::processPvpMapMode") - return; - } - - Map *const map = game->getCurrentMap(); - if (map) - map->setPvpMode(msg.readInt16("pvp mode")); - BLOCK_END("BeingHandler::processPvpMapMode") -} - -void BeingHandler::processBeingMove3(Net::MessageIn &msg) -{ - BLOCK_START("BeingHandler::processBeingMove3") - if (!actorManager || !serverFeatures->haveMove3()) - { - BLOCK_END("BeingHandler::processBeingMove3") - return; - } - - static const int16_t dirx[8] = {0, -1, -1, -1, 0, 1, 1, 1}; - static const int16_t diry[8] = {1, 1, 0, -1, -1, -1, 0, 1}; - - const int len = msg.readInt16("len") - 14; - Being *const dstBeing = actorManager->findBeing( - msg.readBeingId("being id")); - if (!dstBeing) - { - BLOCK_END("BeingHandler::processBeingMove3") - return; - } - const int16_t speed = msg.readInt16("speed"); - dstBeing->setWalkSpeed(Vector(speed, speed, 0)); - int16_t x = msg.readInt16("x"); - int16_t y = msg.readInt16("y"); - const unsigned char *moves = msg.readBytes(len, "moving path"); - Path path; - if (moves) - { - for (int f = 0; f < len; f ++) - { - const unsigned char dir = moves[f]; - if (dir <= 7) - { - x += dirx[dir]; - y += diry[dir]; - path.push_back(Position(x, y)); - } - else - { - logger->log("bad move packet: %d", dir); - } - } - delete [] moves; - } - dstBeing->setPath(path); - BLOCK_END("BeingHandler::processBeingMove3") + BeingNet::mSync = enableSync; + BeingNet::mSpawnId = BeingId_zero; } } // namespace Ea diff --git a/src/net/ea/beinghandler.h b/src/net/ea/beinghandler.h index 6c372ce1f..660f93f8e 100644 --- a/src/net/ea/beinghandler.h +++ b/src/net/ea/beinghandler.h @@ -35,40 +35,6 @@ class BeingHandler notfinal : public Net::BeingHandler protected: explicit BeingHandler(const bool enableSync); - - static Being *createBeing(const BeingId id, - const int job) A_WARN_UNUSED; - - static void setSprite(Being *const being, const unsigned int slot, - const int id, - const std::string &color = "", - const ItemColor colorId = ItemColor_one, - const bool isWeapon = false, - const bool isTempSprite = false); - - static void processBeingRemove(Net::MessageIn &msg); - - static void processSkillDamage(Net::MessageIn &msg); - - static void processBeingAction(Net::MessageIn &msg); - - static void processBeingEmotion(Net::MessageIn &msg); - - static void processNameResponse(Net::MessageIn &msg); - - static void processPlayerStop(Net::MessageIn &msg); - - static void processPlayerMoveToAttack(Net::MessageIn &msg); - - static void processSkillNoDamage(Net::MessageIn &msg); - - static void processPvpMapMode(Net::MessageIn &msg); - - static void processBeingMove3(Net::MessageIn &msg); - - // Should we honor server "Stop Walking" packets - static BeingId mSpawnId; - static bool mSync; }; } // namespace Ea diff --git a/src/net/ea/beingnet.cpp b/src/net/ea/beingnet.cpp new file mode 100644 index 000000000..c7f64aa66 --- /dev/null +++ b/src/net/ea/beingnet.cpp @@ -0,0 +1,473 @@ +/* + * The ManaPlus Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011-2015 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "net/ea/beingnet.h" + +#include "actormanager.h" +#include "game.h" +#include "party.h" + +#include "being/localplayer.h" +#include "being/playerrelation.h" +#include "being/playerrelations.h" + +#include "enums/resources/map/mapitemtype.h" + +#include "gui/viewport.h" + +#include "gui/windows/socialwindow.h" + +#include "resources/map/map.h" + +#include "net/serverfeatures.h" + +#include "debug.h" + +namespace Ea +{ + +namespace BeingNet +{ + bool mSync = false; + BeingId mSpawnId = BeingId_zero; +} // namespace BeingNet + +void BeingNet::processBeingRemove(Net::MessageIn &msg) +{ + BLOCK_START("BeingNet::processBeingRemove") + if (!actorManager || !localPlayer) + { + BLOCK_END("BeingNet::processBeingRemove") + return; + } + + // A being should be removed or has died + + const BeingId id = msg.readBeingId("being id"); + Being *const dstBeing = actorManager->findBeing(id); + if (!dstBeing) + { + msg.readUInt8("dead flag?"); + BLOCK_END("BeingNet::processBeingRemove") + return; + } + + localPlayer->followMoveTo(dstBeing, localPlayer->getNextDestX(), + localPlayer->getNextDestY()); + + // If this is player's current target, clear it. + if (dstBeing == localPlayer->getTarget()) + localPlayer->stopAttack(true); + + if (msg.readUInt8("dead flag?") == 1U) + { + if (dstBeing->getCurrentAction() != BeingAction::DEAD) + { + dstBeing->setAction(BeingAction::DEAD, 0); + dstBeing->recalcSpritesOrder(); + } + } + else + { + if (dstBeing->getType() == ActorType::Player) + { + if (socialWindow) + socialWindow->updateActiveList(); + } + actorManager->destroy(dstBeing); + } + BLOCK_END("BeingNet::processBeingRemove") +} + +void BeingNet::processSkillDamage(Net::MessageIn &msg) +{ + BLOCK_START("BeingNet::processSkillDamage") + if (!actorManager) + { + BLOCK_END("BeingNet::processSkillDamage") + return; + } + + const int id = msg.readInt16("skill id"); + Being *const srcBeing = actorManager->findBeing( + msg.readBeingId("src being id")); + Being *const dstBeing = actorManager->findBeing( + msg.readBeingId("dst being id")); + msg.readInt32("tick"); + msg.readInt32("src speed"); + msg.readInt32("dst speed"); + const int param1 = msg.readInt32("damage"); + const int level = msg.readInt16("skill level"); + msg.readInt16("div"); + msg.readUInt8("skill hit/type?"); + if (srcBeing) + srcBeing->handleSkill(dstBeing, param1, id, level); + if (dstBeing) + dstBeing->takeDamage(srcBeing, param1, AttackType::SKILL, id, level); + BLOCK_END("BeingNet::processSkillDamage") +} + +void BeingNet::processBeingAction(Net::MessageIn &msg) +{ + BLOCK_START("BeingNet::processBeingAction") + if (!actorManager) + { + BLOCK_END("BeingNet::processBeingAction") + return; + } + + Being *const srcBeing = actorManager->findBeing( + msg.readBeingId("src being id")); + Being *const dstBeing = actorManager->findBeing( + msg.readBeingId("dst being id")); + + msg.readInt32("tick"); + const int srcSpeed = msg.readInt32("src speed"); + msg.readInt32("dst speed"); + const int param1 = msg.readInt16("param1"); + msg.readInt16("param 2"); + const AttackTypeT type = static_cast<AttackTypeT>( + msg.readUInt8("type")); + msg.readInt16("param 3"); + + switch (type) + { + case AttackType::HIT: // Damage + case AttackType::CRITICAL: // Critical Damage + case AttackType::MULTI: // Critical Damage + case AttackType::REFLECT: // Reflected Damage + case AttackType::FLEE: // Lucky Dodge + if (srcBeing) + { + if (srcSpeed && srcBeing->getType() == ActorType::Player) + srcBeing->setAttackDelay(srcSpeed); + // attackid=1, type + srcBeing->handleAttack(dstBeing, param1, 1); + if (srcBeing->getType() == ActorType::Player) + srcBeing->setAttackTime(); + } + if (dstBeing) + { + // level not present, using 1 + dstBeing->takeDamage(srcBeing, param1, + static_cast<AttackTypeT>(type), 1); + } + break; + + case AttackType::PICKUP: + break; + // tmw server can send here garbage? +// if (srcBeing) +// srcBeing->setAction(BeingAction::DEAD, 0); + + case AttackType::SIT: + if (srcBeing) + { + srcBeing->setAction(BeingAction::SIT, 0); + if (srcBeing->getType() == ActorType::Player) + { + srcBeing->setMoveTime(); + if (localPlayer) + localPlayer->imitateAction(srcBeing, BeingAction::SIT); + } + } + break; + + case AttackType::STAND: + if (srcBeing) + { + srcBeing->setAction(BeingAction::STAND, 0); + if (srcBeing->getType() == ActorType::Player) + { + srcBeing->setMoveTime(); + if (localPlayer) + { + localPlayer->imitateAction(srcBeing, + BeingAction::STAND); + } + } + } + break; + default: + case AttackType::SPLASH: + case AttackType::SKILL: + case AttackType::REPEATE: + case AttackType::MULTI_REFLECT: + case AttackType::TOUCH_SKILL: + case AttackType::MISS: + case AttackType::SKILLMISS: + UNIMPLIMENTEDPACKET; + break; + } + BLOCK_END("BeingNet::processBeingAction") +} + +void BeingNet::processBeingEmotion(Net::MessageIn &msg) +{ + BLOCK_START("BeingNet::processBeingEmotion") + if (!localPlayer || !actorManager) + { + BLOCK_END("BeingNet::processBeingEmotion") + return; + } + + Being *const dstBeing = actorManager->findBeing( + msg.readBeingId("being id")); + if (!dstBeing) + { + BLOCK_END("BeingNet::processBeingEmotion") + return; + } + + if (player_relations.hasPermission(dstBeing, PlayerRelation::EMOTE)) + { + const uint8_t emote = msg.readUInt8("emote"); + if (emote) + { + dstBeing->setEmote(emote, 0); + localPlayer->imitateEmote(dstBeing, emote); + } + } + if (dstBeing->getType() == ActorType::Player) + dstBeing->setOtherTime(); + BLOCK_END("BeingNet::processBeingEmotion") +} + +void BeingNet::processNameResponse(Net::MessageIn &msg) +{ + BLOCK_START("BeingNet::processNameResponse") + if (!localPlayer || !actorManager) + { + BLOCK_END("BeingNet::processNameResponse") + return; + } + + const BeingId beingId = msg.readBeingId("being id"); + Being *const dstBeing = actorManager->findBeing(beingId); + + if (dstBeing) + { + if (beingId == localPlayer->getId()) + { + localPlayer->pingResponse(); + } + else + { + const std::string name = msg.readString(24, "name"); + if (dstBeing->getType() != ActorType::Portal) + { + dstBeing->setName(name); + } + else if (viewport) + { + Map *const map = viewport->getMap(); + if (map) + { + map->addPortalTile(name, MapItemType::PORTAL, + dstBeing->getTileX(), dstBeing->getTileY()); + } + } + dstBeing->updateGuild(); + dstBeing->addToCache(); + + if (dstBeing->getType() == ActorType::Player) + dstBeing->updateColors(); + + if (localPlayer) + { + const Party *const party = localPlayer->getParty(); + if (party && party->isMember(dstBeing->getId())) + { + PartyMember *const member = party->getMember( + dstBeing->getId()); + + if (member) + member->setName(dstBeing->getName()); + } + localPlayer->checkNewName(dstBeing); + } + BLOCK_END("BeingNet::processNameResponse") + return; + } + } + msg.readString(24, "name"); + BLOCK_END("BeingNet::processNameResponse") +} + +void BeingNet::processPlayerStop(Net::MessageIn &msg) +{ + BLOCK_START("BeingNet::processPlayerStop") + if (!actorManager || !localPlayer) + { + BLOCK_END("BeingNet::processPlayerStop") + return; + } + + const BeingId id = msg.readBeingId("account id"); + + if (mSync || id != localPlayer->getId()) + { + Being *const dstBeing = actorManager->findBeing(id); + if (dstBeing) + { + const uint16_t x = msg.readInt16("x"); + const uint16_t y = msg.readInt16("y"); + dstBeing->setTileCoords(x, y); + if (dstBeing->getCurrentAction() == BeingAction::MOVE) + dstBeing->setAction(BeingAction::STAND, 0); + BLOCK_END("BeingNet::processPlayerStop") + return; + } + } + msg.readInt16("x"); + msg.readInt16("y"); + BLOCK_END("BeingNet::processPlayerStop") +} + +void BeingNet::processPlayerMoveToAttack(Net::MessageIn &msg) +{ + BLOCK_START("BeingNet::processPlayerStop") + msg.readInt32("target id"); + msg.readInt16("target x"); + msg.readInt16("target y"); + msg.readInt16("x"); + msg.readInt16("y"); + msg.readInt16("attack range"); + + if (localPlayer) + localPlayer->fixAttackTarget(); + BLOCK_END("BeingNet::processPlayerStop") +} + +void BeingNet::processSkillNoDamage(Net::MessageIn &msg) +{ + const int id = msg.readInt16("skill id"); + const int heal = msg.readInt16("heal"); + Being *const dstBeing = actorManager->findBeing( + msg.readBeingId("dst being id")); + Being *const srcBeing = actorManager->findBeing( + msg.readBeingId("src being id")); + const int flag = msg.readUInt8("fail"); + + if (srcBeing) + srcBeing->handleSkill(dstBeing, heal, id, 1); +} + +void BeingNet::processPvpMapMode(Net::MessageIn &msg) +{ + BLOCK_START("BeingNet::processPvpMapMode") + const Game *const game = Game::instance(); + if (!game) + { + BLOCK_END("BeingNet::processPvpMapMode") + return; + } + + Map *const map = game->getCurrentMap(); + if (map) + map->setPvpMode(msg.readInt16("pvp mode")); + BLOCK_END("BeingNet::processPvpMapMode") +} + +void BeingNet::processBeingMove3(Net::MessageIn &msg) +{ + BLOCK_START("BeingNet::processBeingMove3") + if (!actorManager || !serverFeatures->haveMove3()) + { + BLOCK_END("BeingNet::processBeingMove3") + return; + } + + static const int16_t dirx[8] = {0, -1, -1, -1, 0, 1, 1, 1}; + static const int16_t diry[8] = {1, 1, 0, -1, -1, -1, 0, 1}; + + const int len = msg.readInt16("len") - 14; + Being *const dstBeing = actorManager->findBeing( + msg.readBeingId("being id")); + if (!dstBeing) + { + BLOCK_END("BeingNet::processBeingMove3") + return; + } + const int16_t speed = msg.readInt16("speed"); + dstBeing->setWalkSpeed(Vector(speed, speed, 0)); + int16_t x = msg.readInt16("x"); + int16_t y = msg.readInt16("y"); + const unsigned char *moves = msg.readBytes(len, "moving path"); + Path path; + if (moves) + { + for (int f = 0; f < len; f ++) + { + const unsigned char dir = moves[f]; + if (dir <= 7) + { + x += dirx[dir]; + y += diry[dir]; + path.push_back(Position(x, y)); + } + else + { + logger->log("bad move packet: %d", dir); + } + } + delete [] moves; + } + dstBeing->setPath(path); + BLOCK_END("BeingNet::processBeingMove3") +} + +Being *BeingNet::createBeing(const BeingId id, + const int job) +{ + if (!actorManager) + return nullptr; + + ActorTypeT type = ActorType::Unknown; + if (job <= 25 || (job >= 4001 && job <= 4049)) + type = ActorType::Player; + else if (job >= 46 && job <= 1000) + type = ActorType::Npc; + else if (job > 1000 && job <= 2000) + type = ActorType::Monster; + else if (job == 45) + type = ActorType::Portal; + + Being *const being = actorManager->createBeing( + id, type, fromInt(job, BeingTypeId)); + return being; +} + +void BeingNet::setSprite(Being *const being, + const unsigned int slot, + const int id, + const std::string &color, + const ItemColor colorId, + const bool isWeapon, + const bool isTempSprite) +{ + if (!being) + return; + being->updateSprite(slot, id, color, colorId, isWeapon, isTempSprite); +} + +} // namespace Ea diff --git a/src/net/ea/beingnet.h b/src/net/ea/beingnet.h new file mode 100644 index 000000000..c32a49b9f --- /dev/null +++ b/src/net/ea/beingnet.h @@ -0,0 +1,59 @@ +/* + * The ManaPlus Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011-2015 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef NET_EA_BEING_H +#define NET_EA_BEING_H + +#include "net/beinghandler.h" + +namespace Ea +{ + namespace BeingNet + { + extern bool mSync; + extern BeingId mSpawnId; + + void processBeingRemove(Net::MessageIn &msg); + void processSkillDamage(Net::MessageIn &msg); + void processBeingAction(Net::MessageIn &msg); + void processBeingEmotion(Net::MessageIn &msg); + void processNameResponse(Net::MessageIn &msg); + void processPlayerStop(Net::MessageIn &msg); + void processPlayerMoveToAttack(Net::MessageIn &msg); + void processSkillNoDamage(Net::MessageIn &msg); + void processPvpMapMode(Net::MessageIn &msg); + void processBeingMove3(Net::MessageIn &msg); + Being *createBeing(const BeingId id, + const int job); + void setSprite(Being *const being, const unsigned int slot, + const int id, + const std::string &color = "", + const ItemColor colorId = ItemColor_one, + const bool isWeapon = false, + const bool isTempSprite = false); + } // namespace Being + +} // namespace Ea + +//extern bool Ea::BeingNet::mSync; + +#endif // NET_EA_BEING_H diff --git a/src/net/eathena/beinghandler.cpp b/src/net/eathena/beinghandler.cpp index 95800dc0d..11a1ced05 100644 --- a/src/net/eathena/beinghandler.cpp +++ b/src/net/eathena/beinghandler.cpp @@ -49,6 +49,9 @@ #include "net/character.h" +#include "net/ea/beingnet.h" + +#include "net/eathena/beingnet.h" #include "net/eathena/maptypeproperty2.h" #include "net/eathena/messageout.h" #include "net/eathena/protocol.h" @@ -158,126 +161,126 @@ void BeingHandler::handleMessage(Net::MessageIn &msg) switch (msg.getId()) { case SMSG_BEING_VISIBLE: - processBeingVisible(msg); + BeingNet::processBeingVisible(msg); break; case SMSG_BEING_MOVE: - processBeingMove(msg); + BeingNet::processBeingMove(msg); break; case SMSG_BEING_SPAWN: - processBeingSpawn(msg); + BeingNet::processBeingSpawn(msg); break; case SMSG_BEING_MOVE2: - processBeingMove2(msg); + BeingNet::processBeingMove2(msg); break; case SMSG_BEING_REMOVE: - processBeingRemove(msg); + Ea::BeingNet::processBeingRemove(msg); break; case SMSG_BEING_REMOVE_SKILL: - processBeingRemoveSkil(msg); + BeingNet::processBeingRemoveSkil(msg); break; case SMSG_BEING_RESURRECT: - processBeingResurrect(msg); + BeingNet::processBeingResurrect(msg); break; case SMSG_SKILL_DAMAGE: - processSkillDamage(msg); + Ea::BeingNet::processSkillDamage(msg); break; case SMSG_SKILL_AUTO_CAST: - processSkillAutoCast(msg); + BeingNet::processSkillAutoCast(msg); break; case SMSG_BEING_ACTION: - processBeingAction(msg); + Ea::BeingNet::processBeingAction(msg); break; case SMSG_BEING_ACTION2: - processBeingAction2(msg); + BeingNet::processBeingAction2(msg); break; case SMSG_BEING_SELFEFFECT: - processBeingSelfEffect(msg); + BeingNet::processBeingSelfEffect(msg); break; case SMSG_BEING_SPECIAL_EFFECT: - processBeingSpecialEffect(msg); + BeingNet::processBeingSpecialEffect(msg); break; case SMSG_BEING_SPECIAL_EFFECT_NUM: - processBeingSpecialEffectNum(msg); + BeingNet::processBeingSpecialEffectNum(msg); break; case SMSG_BEING_SOUND_EFFECT: - processBeingSoundEffect(msg); + BeingNet::processBeingSoundEffect(msg); break; case SMSG_BEING_EMOTION: - processBeingEmotion(msg); + Ea::BeingNet::processBeingEmotion(msg); break; case SMSG_BEING_CHANGE_LOOKS2: - processBeingChangeLook2(msg); + BeingNet::processBeingChangeLook2(msg); break; case SMSG_BEING_CHANGE_LOOKS_CARDS: - processBeingChangeLookCards(msg); + BeingNet::processBeingChangeLookCards(msg); break; case SMSG_BEING_NAME_RESPONSE: - processNameResponse(msg); + Ea::BeingNet::processNameResponse(msg); break; case SMSG_BEING_NAME_RESPONSE2: - processNameResponse2(msg); + BeingNet::processNameResponse2(msg); break; case SMSG_SOLVE_CHAR_NAME: break; case SMSG_PLAYER_GUILD_PARTY_INFO: - processPlayerGuilPartyInfo(msg); + BeingNet::processPlayerGuilPartyInfo(msg); break; case SMSG_BEING_CHANGE_DIRECTION: - processBeingChangeDirection(msg); + BeingNet::processBeingChangeDirection(msg); break; case SMSG_PLAYER_STOP: - processPlayerStop(msg); + Ea::BeingNet::processPlayerStop(msg); break; case SMSG_PLAYER_MOVE_TO_ATTACK: - processPlayerMoveToAttack(msg); + Ea::BeingNet::processPlayerMoveToAttack(msg); break; case SMSG_PLAYER_STATUS_CHANGE: - processPlaterStatusChange(msg); + BeingNet::processPlaterStatusChange(msg); break; case SMSG_PLAYER_STATUS_CHANGE2: - processPlaterStatusChange2(msg); + BeingNet::processPlaterStatusChange2(msg); break; case SMSG_PLAYER_STATUS_CHANGE_NO_TICK: - processPlaterStatusChangeNoTick(msg); + BeingNet::processPlaterStatusChangeNoTick(msg); break; case SMSG_BEING_STATUS_CHANGE: - processBeingStatusChange(msg); + BeingNet::processBeingStatusChange(msg); break; case SMSG_BEING_STATUS_CHANGE2: - processBeingStatusChange2(msg); + BeingNet::processBeingStatusChange2(msg); break; case SMSG_SKILL_CASTING: - processSkillCasting(msg); + BeingNet::processSkillCasting(msg); break; case SMSG_SKILL_CAST_CANCEL: @@ -285,136 +288,136 @@ void BeingHandler::handleMessage(Net::MessageIn &msg) break; case SMSG_SKILL_NO_DAMAGE: - processSkillNoDamage(msg); + Ea::BeingNet::processSkillNoDamage(msg); break; case SMSG_SKILL_GROUND_NO_DAMAGE: - processSkillGroundNoDamage(msg); + BeingNet::processSkillGroundNoDamage(msg); break; case SMSG_SKILL_ENTRY: - processSkillEntry(msg); + BeingNet::processSkillEntry(msg); break; case SMSG_PVP_MAP_MODE: - processPvpMapMode(msg); + Ea::BeingNet::processPvpMapMode(msg); break; case SMSG_PVP_SET: - processPvpSet(msg); + BeingNet::processPvpSet(msg); break; case SMSG_MAP_TYPE_PROPERTY2: - processMapTypeProperty(msg); + BeingNet::processMapTypeProperty(msg); break; case SMSG_MAP_TYPE: - processMapType(msg); + BeingNet::processMapType(msg); break; case SMSG_MONSTER_HP: case SMSG_PLAYER_HP: - processMonsterHp(msg); + BeingNet::processMonsterHp(msg); break; case SMSG_RANKS_LIST: - processRanksList(msg); + BeingNet::processRanksList(msg); break; case SMSG_BEING_FAKE_NAME: - processBeingFakeName(msg); + BeingNet::processBeingFakeName(msg); break; case SMSG_BEING_STAT_UPDATE_1: - processBeingStatUpdate1(msg); + BeingNet::processBeingStatUpdate1(msg); break; case SMSG_MOB_INFO: - processMobInfo(msg); + BeingNet::processMobInfo(msg); break; case SMSG_BEING_MOVE3: - processBeingMove3(msg); + Ea::BeingNet::processBeingMove3(msg); break; case SMSG_BEING_ATTRS: - processBeingAttrs(msg); + BeingNet::processBeingAttrs(msg); break; case SMSG_MONSTER_INFO: - processMonsterInfo(msg); + BeingNet::processMonsterInfo(msg); break; case SMSG_CLASS_CHANGE: - processClassChange(msg); + BeingNet::processClassChange(msg); break; case SMSG_SPIRIT_BALLS: - processSpiritBalls(msg); + BeingNet::processSpiritBalls(msg); break; case SMSG_SPIRIT_BALL_SINGLE: - processSpiritBallSingle(msg); + BeingNet::processSpiritBallSingle(msg); break; case SMSG_BLADE_STOP: - processBladeStop(msg); + BeingNet::processBladeStop(msg); break; case SMSG_COMBO_DELAY: - processComboDelay(msg); + BeingNet::processComboDelay(msg); break; case SMSG_WEDDING_EFFECT: - processWddingEffect(msg); + BeingNet::processWddingEffect(msg); break; case SMSG_BEING_SLIDE: - processBeingSlide(msg); + BeingNet::processBeingSlide(msg); break; case SMSG_STARS_KILL: - processStarsKill(msg); + BeingNet::processStarsKill(msg); break; case SMSG_BLACKSMITH_RANKS_LIST: - processBlacksmithRanksList(msg); + BeingNet::processBlacksmithRanksList(msg); break; case SMSG_ALCHEMIST_RANKS_LIST: - processAlchemistRanksList(msg); + BeingNet::processAlchemistRanksList(msg); break; case SMSG_TAEKWON_RANKS_LIST: - processTaekwonRanksList(msg); + BeingNet::processTaekwonRanksList(msg); break; case SMSG_PK_RANKS_LIST: - processPkRanksList(msg); + BeingNet::processPkRanksList(msg); break; case SMSG_GLADIATOR_FEEL_REQUEST: - processGladiatorFeelRequest(msg); + BeingNet::processGladiatorFeelRequest(msg); break; case SMSG_BOSS_MAP_INFO: - processBossMapInfo(msg); + BeingNet::processBossMapInfo(msg); break; case SMSG_BEING_FONT: - processBeingFont(msg); + BeingNet::processBeingFont(msg); break; case SMSG_BEING_MILLENIUM_SHIELD: - processBeingMilleniumShield(msg); + BeingNet::processBeingMilleniumShield(msg); break; case SMSG_BEING_CHARM: - processBeingCharm(msg); + BeingNet::processBeingCharm(msg); break; case SMSG_BEING_VIEW_EQUIPMENT: - processBeingViewEquipment(msg); + BeingNet::processBeingViewEquipment(msg); break; default: @@ -422,69 +425,6 @@ void BeingHandler::handleMessage(Net::MessageIn &msg) } } -Being *BeingHandler::createBeing2(Net::MessageIn &msg, - const BeingId id, - const int16_t job, - const BeingType::BeingType beingType) -{ - if (!actorManager) - return nullptr; - - ActorTypeT type = ActorType::Unknown; - switch (beingType) - { - case BeingType::PC: - type = ActorType::Player; - break; - case BeingType::NPC: - case BeingType::NPC_EVENT: - type = ActorType::Npc; - break; - case BeingType::MONSTER: - type = ActorType::Monster; - break; - case BeingType::MERSOL: - type = ActorType::Mercenary; - break; - case BeingType::PET: - type = ActorType::Pet; - break; - case BeingType::HOMUN: - type = ActorType::Homunculus; - break; - case BeingType::ITEM: - case BeingType::SKILL: - case BeingType::ELEMENTAL: - logger->log("not supported object type: %d, job: %d", - static_cast<int>(beingType), static_cast<int>(job)); - break; - case BeingType::CHAT: - default: - UNIMPLIMENTEDPACKET; - type = ActorType::Monster; - logger->log("not supported object type: %d, job: %d", - static_cast<int>(beingType), static_cast<int>(job)); - break; - } - if (job == 45 && beingType == BeingType::NPC_EVENT) - type = ActorType::Portal; - - Being *const being = actorManager->createBeing( - id, type, fromInt(job, BeingTypeId)); - if (beingType == BeingType::MERSOL) - { - MercenaryInfo *const info = PlayerInfo::getMercenary(); - if (info && info->id == id) - PlayerInfo::setMercenaryBeing(being); - } - else if (beingType == BeingType::PET) - { - if (PlayerInfo::getPetBeingId() == id) - PlayerInfo::setPetBeing(being); - } - return being; -} - void BeingHandler::undress(Being *const being) const { if (!being) @@ -503,1074 +443,6 @@ void BeingHandler::requestRanks(const RankT rank) const outMsg.writeInt16(static_cast<int16_t>(rank), "type"); } -void BeingHandler::processBeingChangeLook2(Net::MessageIn &msg) -{ - if (!actorManager) - return; - - Being *const dstBeing = actorManager->findBeing( - msg.readBeingId("being id")); - const uint8_t type = msg.readUInt8("type"); - - const int id = msg.readInt16("id1"); - unsigned int id2 = msg.readInt16("id2"); - if (type != 2) - id2 = 1; - - if (!localPlayer || !dstBeing) - return; - - processBeingChangeLookContinue(msg, dstBeing, type, id, id2, nullptr); -} - -void BeingHandler::processBeingChangeLookCards(Net::MessageIn &msg) -{ - Being *dstBeing = nullptr; - int cards[4]; - - if (!actorManager) - { // here can be look from char server - if (!serverFeatures->haveAdvancedSprites()) - return; - Net::Characters &chars = Net::CharServerHandler::mCharacters; - const BeingId id = msg.readBeingId("being id"); - - FOR_EACH (Net::Characters::iterator, it, chars) - { - Net::Character *character = *it; - if (character->dummy && character->dummy->getId() == id) - { - dstBeing = character->dummy; - break; - } - } - } - else - { - dstBeing = actorManager->findBeing( - msg.readBeingId("being id")); - } - - const uint8_t type = msg.readUInt8("type"); - - const int id = msg.readInt16("id1"); - unsigned int id2 = msg.readInt16("id2"); - if (type != 2) - id2 = 1; - - for (int f = 0; f < 4; f ++) - cards[f] = msg.readInt16("card"); - - if (!dstBeing) - return; - - processBeingChangeLookContinue(msg, dstBeing, type, id, id2, &cards[0]); -} - -void BeingHandler::processBeingChangeLookContinue(Net::MessageIn &msg, - Being *const dstBeing, - const uint8_t type, - const int id, - const int id2, - const int *cards - A_UNUSED) -{ - if (dstBeing->getType() == ActorType::Player) - dstBeing->setOtherTime(); - - const ItemColor itemColor = ItemColorManager::getColorFromCards(cards); - const std::string color; - switch (type) - { - case 0: // change race - dstBeing->setSubtype(fromInt(id, BeingTypeId), - dstBeing->getLook()); - break; - case 1: // eAthena LOOK_HAIR - dstBeing->setHairColor(fromInt(id, ItemColor)); - dstBeing->setSpriteID(SPRITE_HAIR_COLOR, id * -1); - break; - case 2: // LOOK_WEAPON Weapon ID in id, Shield ID in id2 - dstBeing->setSprite(SPRITE_BODY, id, "", itemColor, true); - dstBeing->setSprite(SPRITE_FLOOR, id2); - if (localPlayer) - localPlayer->imitateOutfit(dstBeing, SPRITE_FLOOR); - break; - case 3: // LOOK_HEAD_BOTTOM - dstBeing->setSprite(SPRITE_WEAPON, id, color, itemColor); - if (localPlayer) - localPlayer->imitateOutfit(dstBeing, SPRITE_WEAPON); - break; - case 4: // LOOK_HEAD_TOP Change upper headgear for eAthena, hat for us - dstBeing->setSprite(SPRITE_CLOTHES_COLOR, id, color, itemColor); - if (localPlayer) - localPlayer->imitateOutfit(dstBeing, SPRITE_CLOTHES_COLOR); - break; - case 5: // LOOK_HEAD_MID Change middle headgear for eathena, - // armor for us - dstBeing->setSprite(SPRITE_HEAD_BOTTOM, id, color, itemColor); - if (localPlayer) - localPlayer->imitateOutfit(dstBeing, SPRITE_HEAD_BOTTOM); - break; - case 6: // eAthena LOOK_HAIR_COLOR - dstBeing->setHairColor(fromInt(id, ItemColor)); - dstBeing->setSpriteColor(SPRITE_HAIR_COLOR, - ItemDB::get(dstBeing->getSpriteID( - SPRITE_HAIR_COLOR)).getDyeColorsString( - fromInt(id, ItemColor))); - break; - case 7: // Clothes color. Now used as look - dstBeing->setLook(static_cast<uint8_t>(id)); - break; - case 8: // eAthena LOOK_SHIELD - dstBeing->setSprite(SPRITE_FLOOR, id, color, itemColor); - if (localPlayer) - localPlayer->imitateOutfit(dstBeing, SPRITE_FLOOR); - break; - case 9: // eAthena LOOK_SHOES - dstBeing->setSprite(SPRITE_HAIR, id, color, itemColor); - if (localPlayer) - localPlayer->imitateOutfit(dstBeing, SPRITE_HAIR); - break; - case 10: // LOOK_GLOVES - dstBeing->setSprite(SPRITE_SHOES, id, color, itemColor); - if (localPlayer) - localPlayer->imitateOutfit(dstBeing, SPRITE_SHOES); - break; - case 11: // LOOK_FLOOR - dstBeing->setSprite(SPRITE_SHIELD, id, color, itemColor); - if (localPlayer) - localPlayer->imitateOutfit(dstBeing, SPRITE_SHIELD); - break; - case 12: // LOOK_ROBE - dstBeing->setSprite(SPRITE_HEAD_TOP, id, color, itemColor); - if (localPlayer) - localPlayer->imitateOutfit(dstBeing, SPRITE_HEAD_TOP); - break; - case 13: // COSTUME_HEAD_TOP - dstBeing->setSprite(SPRITE_HEAD_MID, id, color, itemColor); - if (localPlayer) - localPlayer->imitateOutfit(dstBeing, SPRITE_HEAD_MID); - break; - case 14: // COSTUME_HEAD_MID - dstBeing->setSprite(SPRITE_ROBE, id, color, itemColor); - if (localPlayer) - localPlayer->imitateOutfit(dstBeing, SPRITE_ROBE); - break; - case 15: // COSTUME_HEAD_LOW - dstBeing->setSprite(SPRITE_EVOL2, id, color, itemColor); - if (localPlayer) - localPlayer->imitateOutfit(dstBeing, SPRITE_EVOL2); - break; - case 16: // COSTUME_GARMENT - dstBeing->setSprite(SPRITE_EVOL3, id, color, itemColor); - if (localPlayer) - localPlayer->imitateOutfit(dstBeing, SPRITE_EVOL3); - break; - case 17: // ARMOR - dstBeing->setSprite(SPRITE_EVOL4, id, color, itemColor); - if (localPlayer) - localPlayer->imitateOutfit(dstBeing, SPRITE_EVOL4); - break; - default: - UNIMPLIMENTEDPACKET; - break; - } -} - -void BeingHandler::processBeingVisible(Net::MessageIn &msg) -{ - if (!actorManager) - return; - - msg.readInt16("len"); - const BeingType::BeingType type = static_cast<BeingType::BeingType>( - msg.readUInt8("object type")); - - // Information about a being in range - const BeingId id = msg.readBeingId("being id"); - BeingId spawnId; - if (id == mSpawnId) - spawnId = mSpawnId; - else - spawnId = BeingId_zero; - mSpawnId = BeingId_zero; - - int16_t speed = msg.readInt16("speed"); - const uint16_t stunMode = msg.readInt16("opt1"); - // probably wrong effect usage - const uint32_t statusEffects = msg.readInt16("opt2"); - msg.readInt32("option"); - - const int16_t job = msg.readInt16("class"); - - Being *dstBeing = actorManager->findBeing(id); - - if (dstBeing && dstBeing->getType() == ActorType::Monster - && !dstBeing->isAlive()) - { - actorManager->destroy(dstBeing); - actorManager->erase(dstBeing); - dstBeing = nullptr; - } - - if (!dstBeing) - { - if (actorManager->isBlocked(id) == true) - return; - - dstBeing = createBeing2(msg, id, job, type); - if (!dstBeing) - return; - } - else - { - // undeleting marked for deletion being - if (dstBeing->getType() == ActorType::Npc) - actorManager->undelete(dstBeing); - } - - if (dstBeing->getType() == ActorType::Player) - dstBeing->setMoveTime(); - - if (spawnId != BeingId_zero) - { - dstBeing->setAction(BeingAction::SPAWN, 0); - } - else - { - dstBeing->clearPath(); - dstBeing->setActionTime(tick_time); - dstBeing->setAction(BeingAction::STAND, 0); - } - - // Prevent division by 0 when calculating frame - if (speed == 0) - speed = 150; - - dstBeing->setWalkSpeed(Vector(speed, speed, 0)); - dstBeing->setSubtype(fromInt(job, BeingTypeId), 0); - if (dstBeing->getType() == ActorType::Monster && localPlayer) - localPlayer->checkNewName(dstBeing); - - const int hairStyle = msg.readInt16("hair style"); - const uint32_t weapon = static_cast<uint32_t>(msg.readInt32("weapon")); - const uint16_t headBottom = msg.readInt16("head bottom"); - - const uint16_t headTop = msg.readInt16("head top"); - const uint16_t headMid = msg.readInt16("head mid"); - const ItemColor hairColor = fromInt(msg.readInt16("hair color"), ItemColor); - const uint16_t shoes = msg.readInt16("shoes or clothes color?"); - - const uint16_t gloves = msg.readInt16("head dir / gloves"); - // may be use robe as gloves? - msg.readInt16("robe"); - msg.readInt32("guild id"); - msg.readInt16("guild emblem"); - dstBeing->setManner(msg.readInt16("manner")); - dstBeing->setStatusEffectBlock(32, static_cast<uint16_t>( - msg.readInt32("opt3"))); - dstBeing->setKarma(msg.readUInt8("karma")); - uint8_t gender = static_cast<uint8_t>(msg.readUInt8("gender") & 3); - - if (dstBeing->getType() == ActorType::Player) - { - dstBeing->setGender(Being::intToGender(gender)); - dstBeing->setHairColor(hairColor); - // Set these after the gender, as the sprites may be gender-specific - setSprite(dstBeing, SPRITE_HAIR_COLOR, hairStyle * -1, - ItemDB::get(-hairStyle).getDyeColorsString(hairColor)); - setSprite(dstBeing, SPRITE_WEAPON, headBottom); - setSprite(dstBeing, SPRITE_HEAD_BOTTOM, headMid); - setSprite(dstBeing, SPRITE_CLOTHES_COLOR, headTop); - setSprite(dstBeing, SPRITE_HAIR, shoes); - setSprite(dstBeing, SPRITE_SHOES, gloves); - setSprite(dstBeing, SPRITE_BODY, weapon, "", ItemColor_one, true); -// setSprite(dstBeing, SPRITE_FLOOR, shield); - } - else if (dstBeing->getType() == ActorType::Npc - && serverFeatures->haveNpcGender()) - { - dstBeing->setGender(Being::intToGender(gender)); - } - - uint8_t dir; - uint16_t x, y; - msg.readCoordinates(x, y, dir, "position"); - msg.readInt8("xs"); - msg.readInt8("ys"); - msg.readUInt8("action type"); - dstBeing->setTileCoords(x, y); - - if (job == 45 && socialWindow && outfitWindow) - { - const int num = socialWindow->getPortalIndex(x, y); - if (num >= 0) - { - dstBeing->setName(keyboard.getKeyShortString( - outfitWindow->keyName(num))); - } - else - { - dstBeing->setName(""); - } - } - - dstBeing->setDirection(dir); - - const int level = static_cast<int>(msg.readInt16("level")); - if (level) - dstBeing->setLevel(level); - msg.readInt16("font"); - - const int maxHP = msg.readInt32("max hp"); - const int hp = msg.readInt32("hp"); - dstBeing->setMaxHP(maxHP); - dstBeing->setHP(hp); - - msg.readInt8("is boss"); - - dstBeing->setStunMode(stunMode); - dstBeing->setStatusEffectBlock(0, static_cast<uint16_t>( - (statusEffects >> 16) & 0xffffU)); - dstBeing->setStatusEffectBlock(16, static_cast<uint16_t>( - statusEffects & 0xffffU)); -} - -void BeingHandler::processBeingMove(Net::MessageIn &msg) -{ - if (!actorManager) - return; - - msg.readInt16("len"); - const BeingType::BeingType type = static_cast<BeingType::BeingType>( - msg.readUInt8("object type")); - - // Information about a being in range - const BeingId id = msg.readBeingId("being id"); - BeingId spawnId; - if (id == mSpawnId) - spawnId = mSpawnId; - else - spawnId = BeingId_zero; - mSpawnId = BeingId_zero; - int16_t speed = msg.readInt16("speed"); -// if (visible) -// { - const uint16_t stunMode = msg.readInt16("opt1"); - // probably wrong effect usage - const uint32_t statusEffects = msg.readInt16("opt2"); -// } -// else -// { -// commented for now, probably it can be removed after testing -// msg.readInt16("body state"); -// msg.readInt16("health state"); -// } - msg.readInt32("effect state"); - - const int16_t job = msg.readInt16("class"); - - Being *dstBeing = actorManager->findBeing(id); - - if (dstBeing && dstBeing->getType() == ActorType::Monster - && !dstBeing->isAlive()) - { - actorManager->destroy(dstBeing); - actorManager->erase(dstBeing); - dstBeing = nullptr; - } - - if (!dstBeing) - { - if (actorManager->isBlocked(id) == true) - return; - - dstBeing = createBeing2(msg, id, job, type); - if (!dstBeing) - return; - } - else - { - // undeleting marked for deletion being - if (dstBeing->getType() == ActorType::Npc) - actorManager->undelete(dstBeing); - } - - if (dstBeing->getType() == ActorType::Player) - dstBeing->setMoveTime(); - - if (spawnId != BeingId_zero) - dstBeing->setAction(BeingAction::SPAWN, 0); - - // Prevent division by 0 when calculating frame - if (speed == 0) - speed = 150; - - dstBeing->setWalkSpeed(Vector(speed, speed, 0)); - dstBeing->setSubtype(fromInt(job, BeingTypeId), 0); - if (dstBeing->getType() == ActorType::Monster && localPlayer) - localPlayer->checkNewName(dstBeing); - - const int hairStyle = msg.readInt16("hair style"); - const uint32_t weapon = static_cast<uint32_t>(msg.readInt32("weapon")); - const uint16_t headBottom = msg.readInt16("head bottom"); - - msg.readInt32("tick"); - - const uint16_t headTop = msg.readInt16("head top"); - const uint16_t headMid = msg.readInt16("head mid"); - const ItemColor hairColor = fromInt( - msg.readInt16("hair color"), ItemColor); - const uint16_t shoes = msg.readInt16("shoes or clothes color?"); - - const uint16_t gloves = msg.readInt16("head dir / gloves"); - // may be use robe as gloves? - msg.readInt16("robe"); - msg.readInt32("guild id"); - msg.readInt16("guild emblem"); - dstBeing->setManner(msg.readInt16("manner")); - dstBeing->setStatusEffectBlock(32, static_cast<uint16_t>( - msg.readInt32("opt3"))); - dstBeing->setKarma(msg.readUInt8("karma")); - uint8_t gender = static_cast<uint8_t>(msg.readUInt8("gender") & 3); - - if (dstBeing->getType() == ActorType::Player) - { - dstBeing->setGender(Being::intToGender(gender)); - dstBeing->setHairColor(hairColor); - // Set these after the gender, as the sprites may be gender-specific - setSprite(dstBeing, SPRITE_HAIR_COLOR, hairStyle * -1, - ItemDB::get(-hairStyle).getDyeColorsString(hairColor)); - if (!serverFeatures->haveMove3()) - { - setSprite(dstBeing, SPRITE_WEAPON, headBottom); - setSprite(dstBeing, SPRITE_HEAD_BOTTOM, headMid); - setSprite(dstBeing, SPRITE_CLOTHES_COLOR, headTop); - setSprite(dstBeing, SPRITE_HAIR, shoes); - setSprite(dstBeing, SPRITE_SHOES, gloves); - setSprite(dstBeing, SPRITE_BODY, weapon, "", ItemColor_one, true); - } -// setSprite(dstBeing, SPRITE_FLOOR, shield); - } - else if (dstBeing->getType() == ActorType::Npc - && serverFeatures->haveNpcGender()) - { - dstBeing->setGender(Being::intToGender(gender)); - } - - uint16_t srcX, srcY, dstX, dstY; - msg.readCoordinatePair(srcX, srcY, dstX, dstY, "move path"); - msg.readUInt8("(sx<<4) | (sy&0x0f)"); - msg.readInt8("xs"); - msg.readInt8("ys"); - dstBeing->setAction(BeingAction::STAND, 0); - dstBeing->setTileCoords(srcX, srcY); - if (localPlayer) - localPlayer->followMoveTo(dstBeing, srcX, srcY, dstX, dstY); - if (!serverFeatures->haveMove3()) - dstBeing->setDestination(dstX, dstY); - - // because server don't send direction in move packet, we fixing it - - uint8_t d = 0; - if (srcX == dstX && srcY == dstY) - { // if player did one step from invisible area to visible, - // move path is broken - int x2 = localPlayer->getTileX(); - int y2 = localPlayer->getTileY(); - if (abs(x2 - srcX) > abs(y2 - srcY)) - y2 = srcY; - else - x2 = srcX; - d = dstBeing->calcDirection(x2, y2); - } - else - { - d = dstBeing->calcDirection(dstX, dstY); - } - if (d && dstBeing->getDirection() != d) - dstBeing->setDirection(d); - - const int level = static_cast<int>(msg.readInt16("level")); - if (level) - dstBeing->setLevel(level); - msg.readInt16("font"); - - const int maxHP = msg.readInt32("max hp"); - const int hp = msg.readInt32("hp"); - dstBeing->setMaxHP(maxHP); - dstBeing->setHP(hp); - - msg.readInt8("is boss"); - - dstBeing->setStunMode(stunMode); - dstBeing->setStatusEffectBlock(0, static_cast<uint16_t>( - (statusEffects >> 16) & 0xffffU)); - dstBeing->setStatusEffectBlock(16, static_cast<uint16_t>( - statusEffects & 0xffffU)); -} - -void BeingHandler::processBeingSpawn(Net::MessageIn &msg) -{ - if (!actorManager) - return; - - msg.readInt16("len"); - const BeingType::BeingType type = static_cast<BeingType::BeingType>( - msg.readUInt8("object type")); - - // Information about a being in range - const BeingId id = msg.readBeingId("being id"); - mSpawnId = id; - const BeingId spawnId = id; - int16_t speed = msg.readInt16("speed"); -// if (visible) -// { - const uint16_t stunMode = msg.readInt16("opt1"); - // probably wrong effect usage - const uint32_t statusEffects = msg.readInt16("opt2"); -// } -// else -// { -// commented for now, probably it can be removed after testing -// msg.readInt16("body state"); -// msg.readInt16("health state"); -// } - msg.readInt32("effect state"); - - const int16_t job = msg.readInt16("class"); - - Being *dstBeing = actorManager->findBeing(id); - - if (dstBeing && dstBeing->getType() == ActorType::Monster - && !dstBeing->isAlive()) - { - actorManager->destroy(dstBeing); - actorManager->erase(dstBeing); - dstBeing = nullptr; - } - - if (!dstBeing) - { - if (actorManager->isBlocked(id) == true) - return; - - dstBeing = createBeing2(msg, id, job, type); - if (!dstBeing) - return; - } - else - { - // undeleting marked for deletion being - if (dstBeing->getType() == ActorType::Npc) - actorManager->undelete(dstBeing); - } - - if (dstBeing->getType() == ActorType::Player) - dstBeing->setMoveTime(); - - if (spawnId != BeingId_zero) - dstBeing->setAction(BeingAction::SPAWN, 0); - - // Prevent division by 0 when calculating frame - if (speed == 0) - speed = 150; - - dstBeing->setWalkSpeed(Vector(speed, speed, 0)); - dstBeing->setSubtype(fromInt(job, BeingTypeId), 0); - if (dstBeing->getType() == ActorType::Monster && localPlayer) - localPlayer->checkNewName(dstBeing); - - const int hairStyle = msg.readInt16("hair style"); - const uint32_t weapon = static_cast<uint32_t>(msg.readInt32("weapon")); - const uint16_t headBottom = msg.readInt16("head bottom"); - - const uint16_t headTop = msg.readInt16("head top"); - const uint16_t headMid = msg.readInt16("head mid"); - const ItemColor hairColor = fromInt( - msg.readInt16("hair color"), ItemColor); - const uint16_t shoes = msg.readInt16("shoes or clothes color?"); - const uint16_t gloves = msg.readInt16("head dir / gloves"); - // may be use robe as gloves? - msg.readInt16("robe"); - msg.readInt32("guild id"); - msg.readInt16("guild emblem"); - dstBeing->setManner(msg.readInt16("manner")); - dstBeing->setStatusEffectBlock(32, static_cast<uint16_t>( - msg.readInt32("opt3"))); - dstBeing->setKarma(msg.readUInt8("karma")); - uint8_t gender = static_cast<uint8_t>(msg.readUInt8("gender") & 3); - - if (dstBeing->getType() == ActorType::Player) - { - dstBeing->setGender(Being::intToGender(gender)); - dstBeing->setHairColor(hairColor); - // Set these after the gender, as the sprites may be gender-specific - setSprite(dstBeing, SPRITE_HAIR_COLOR, hairStyle * -1, - ItemDB::get(-hairStyle).getDyeColorsString(hairColor)); - setSprite(dstBeing, SPRITE_WEAPON, headBottom); - setSprite(dstBeing, SPRITE_HEAD_BOTTOM, headMid); - setSprite(dstBeing, SPRITE_CLOTHES_COLOR, headTop); - setSprite(dstBeing, SPRITE_HAIR, shoes); - setSprite(dstBeing, SPRITE_SHOES, gloves); - setSprite(dstBeing, SPRITE_BODY, weapon, "", ItemColor_one, true); -// setSprite(dstBeing, SPRITE_FLOOR, shield); - } - else if (dstBeing->getType() == ActorType::Npc - && serverFeatures->haveNpcGender()) - { - dstBeing->setGender(Being::intToGender(gender)); - } - - uint8_t dir; - uint16_t x, y; - msg.readCoordinates(x, y, dir, "position"); - msg.readInt8("xs"); - msg.readInt8("ys"); - dstBeing->setTileCoords(x, y); - - if (job == 45 && socialWindow && outfitWindow) - { - const int num = socialWindow->getPortalIndex(x, y); - if (num >= 0) - { - dstBeing->setName(keyboard.getKeyShortString( - outfitWindow->keyName(num))); - } - else - { - dstBeing->setName(""); - } - } - - dstBeing->setDirection(dir); - - const int level = static_cast<int>(msg.readInt16("level")); - if (level) - dstBeing->setLevel(level); - msg.readInt16("font"); - - const int maxHP = msg.readInt32("max hp"); - const int hp = msg.readInt32("hp"); - dstBeing->setMaxHP(maxHP); - dstBeing->setHP(hp); - - msg.readInt8("is boss"); - - dstBeing->setStunMode(stunMode); - dstBeing->setStatusEffectBlock(0, static_cast<uint16_t>( - (statusEffects >> 16) & 0xffffU)); - dstBeing->setStatusEffectBlock(16, static_cast<uint16_t>( - statusEffects & 0xffffU)); -} - -void BeingHandler::processMapTypeProperty(Net::MessageIn &msg) -{ - const int16_t type = msg.readInt16("type"); - const int flags = msg.readInt32("flags"); - if (type == 0x28) - { - // +++ need get other flags from here - MapTypeProperty2 props; - props.data = static_cast<uint32_t>(flags); - Game *const game = Game::instance(); - if (!game) - return; - Map *const map = game->getCurrentMap(); - if (!map) - return; - map->setPvpMode(props.bits.party | (props.bits.guild * 2)); - } -} - -void BeingHandler::processMapType(Net::MessageIn &msg) -{ - const int16_t type = msg.readInt16("type"); - if (type == 19) - NotifyManager::notify(NotifyTypes::MAP_TYPE_BATTLEFIELD); - else - UNIMPLIMENTEDPACKET; -} - -void BeingHandler::processSkillCasting(Net::MessageIn &msg) -{ - // +++ need use other parameters - - const BeingId srcId = msg.readBeingId("src id"); - const BeingId dstId = msg.readBeingId("dst id"); - const int dstX = msg.readInt16("dst x"); - const int dstY = msg.readInt16("dst y"); - const int skillId = msg.readInt16("skill id"); - msg.readInt32("property"); // can be used to trigger effect - msg.readInt32("cast time"); - msg.readInt8("dispossable"); - - if (!effectManager) - return; - - if (srcId == BeingId_zero) - { - UNIMPLIMENTEDPACKET; - return; - } - else if (dstId != BeingId_zero) - { // being to being - Being *const srcBeing = actorManager->findBeing(srcId); - Being *const dstBeing = actorManager->findBeing(dstId); - skillDialog->playCastingSrcEffect(skillId, srcBeing); - skillDialog->playCastingDstEffect(skillId, dstBeing); - } - else if (dstX != 0 || dstY != 0) - { // being to position - UNIMPLIMENTEDPACKET; - } -} - -void BeingHandler::processBeingStatusChange(Net::MessageIn &msg) -{ - BLOCK_START("BeingHandler::processBeingStatusChange") - if (!actorManager) - { - BLOCK_END("BeingHandler::processBeingStatusChange") - return; - } - - // Status change - const uint16_t status = msg.readInt16("status"); - const BeingId id = msg.readBeingId("being id"); - const Enable flag = fromBool( - msg.readUInt8("flag: 0: stop, 1: start"), Enable); - msg.readInt32("total"); - msg.readInt32("left"); - msg.readInt32("val1"); - msg.readInt32("val2"); - msg.readInt32("val3"); - - Being *const dstBeing = actorManager->findBeing(id); - if (dstBeing) - dstBeing->setStatusEffect(status, flag); - BLOCK_END("BeingHandler::processBeingStatusChange") -} - -void BeingHandler::processBeingStatusChange2(Net::MessageIn &msg) -{ - BLOCK_START("BeingHandler::processBeingStatusChange") - if (!actorManager) - { - BLOCK_END("BeingHandler::processBeingStatusChange") - return; - } - - // Status change - const uint16_t status = msg.readInt16("status"); - const BeingId id = msg.readBeingId("being id"); - const Enable flag = fromBool( - msg.readUInt8("flag: 0: stop, 1: start"), Enable); - msg.readInt32("left"); - msg.readInt32("val1"); - msg.readInt32("val2"); - msg.readInt32("val3"); - - Being *const dstBeing = actorManager->findBeing(id); - if (dstBeing) - dstBeing->setStatusEffect(status, flag); - BLOCK_END("BeingHandler::processBeingStatusChange") -} - -void BeingHandler::processBeingMove2(Net::MessageIn &msg) -{ - BLOCK_START("BeingHandler::processBeingMove2") - if (!actorManager) - { - BLOCK_END("BeingHandler::processBeingMove2") - return; - } - - /* - * A simplified movement packet, used by the - * later versions of eAthena for both mobs and - * players - */ - Being *const dstBeing = actorManager->findBeing( - msg.readBeingId("being id")); - - uint16_t srcX, srcY, dstX, dstY; - msg.readCoordinatePair(srcX, srcY, dstX, dstY, "move path"); - msg.readUInt8("(sx<<4) | (sy&0x0f)"); - msg.readInt32("tick"); - - /* - * This packet doesn't have enough info to actually - * create a new being, so if the being isn't found, - * we'll just pretend the packet didn't happen - */ - - if (!dstBeing) - { - BLOCK_END("BeingHandler::processBeingMove2") - return; - } - - dstBeing->setTileCoords(srcX, srcY); - if (localPlayer) - localPlayer->followMoveTo(dstBeing, srcX, srcY, dstX, dstY); - if (!serverFeatures->haveMove3()) - dstBeing->setDestination(dstX, dstY); - if (dstBeing->getType() == ActorType::Player) - dstBeing->setMoveTime(); - BLOCK_END("BeingHandler::processBeingMove2") -} - -void BeingHandler::processBeingAction2(Net::MessageIn &msg) -{ - BLOCK_START("BeingHandler::processBeingAction2") - if (!actorManager) - { - BLOCK_END("BeingHandler::processBeingAction2") - return; - } - - Being *const srcBeing = actorManager->findBeing( - msg.readBeingId("src being id")); - Being *const dstBeing = actorManager->findBeing( - msg.readBeingId("dst being id")); - - msg.readInt32("tick"); - const int srcSpeed = msg.readInt32("src speed"); - msg.readInt32("dst speed"); - const int param1 = msg.readInt32("damage"); - msg.readInt16("count"); - const AttackTypeT type = static_cast<AttackTypeT>( - msg.readUInt8("action")); - msg.readInt32("left damage"); - - switch (type) - { - case AttackType::HIT: // Damage - case AttackType::CRITICAL: // Critical Damage - case AttackType::MULTI: // Critical Damage - case AttackType::MULTI_REFLECT: - case AttackType::REFLECT: // Reflected Damage - case AttackType::FLEE: // Lucky Dodge - case AttackType::SPLASH: - case AttackType::SKILL: - case AttackType::REPEATE: - if (srcBeing) - { - if (srcSpeed && srcBeing->getType() == ActorType::Player) - srcBeing->setAttackDelay(srcSpeed); - // attackid=1, type - srcBeing->handleAttack(dstBeing, param1, 1); - if (srcBeing->getType() == ActorType::Player) - srcBeing->setAttackTime(); - } - if (dstBeing) - { - // level not present, using 1 - dstBeing->takeDamage(srcBeing, param1, - static_cast<AttackTypeT>(type), 1); - } - break; - - case AttackType::PICKUP: - break; - - case AttackType::TOUCH_SKILL: - break; - - case AttackType::SIT: - if (srcBeing) - { - srcBeing->setAction(BeingAction::SIT, 0); - if (srcBeing->getType() == ActorType::Player) - { - srcBeing->setMoveTime(); - if (localPlayer) - localPlayer->imitateAction(srcBeing, BeingAction::SIT); - } - } - break; - - case AttackType::STAND: - if (srcBeing) - { - srcBeing->setAction(BeingAction::STAND, 0); - if (srcBeing->getType() == ActorType::Player) - { - srcBeing->setMoveTime(); - if (localPlayer) - { - localPlayer->imitateAction(srcBeing, - BeingAction::STAND); - } - } - } - break; - default: - case AttackType::MISS: - case AttackType::SKILLMISS: - UNIMPLIMENTEDPACKET; - break; - } - BLOCK_END("BeingHandler::processBeingAction2") -} - -void BeingHandler::processMonsterHp(Net::MessageIn &msg) -{ - Being *const dstBeing = actorManager->findBeing( - msg.readBeingId("monster id")); - const int hp = msg.readInt32("hp"); - const int maxHP = msg.readInt32("max hp"); - if (dstBeing) - { - dstBeing->setHP(hp); - dstBeing->setMaxHP(maxHP); - } -} - -void BeingHandler::processSkillAutoCast(Net::MessageIn &msg) -{ - const int id = msg.readInt16("skill id"); - const int type = msg.readInt16("inf"); - msg.readInt16("unused"); - const int level = msg.readInt16("skill level"); - const int sp = msg.readInt16("sp"); - const int range = msg.readInt16("range"); - msg.readString(24, "skill name"); - msg.readInt8("unused"); - - if (localPlayer) - { - localPlayer->handleSkill(localPlayer, 0, id, level); - localPlayer->takeDamage(localPlayer, 0, AttackType::SKILL, id, level); - } -} - -void BeingHandler::processRanksList(Net::MessageIn &msg) -{ - UNIMPLIMENTEDPACKET; - // +++ here need window with rank tables. - msg.readInt16("rank type"); - for (int f = 0; f < 10; f ++) - msg.readString(24, "name"); - for (int f = 0; f < 10; f ++) - msg.readInt32("points"); - msg.readInt32("my points"); -} - -void BeingHandler::processBlacksmithRanksList(Net::MessageIn &msg) -{ - UNIMPLIMENTEDPACKET; - // +++ here need window with rank tables. - for (int f = 0; f < 10; f ++) - msg.readString(24, "name"); - for (int f = 0; f < 10; f ++) - msg.readInt32("points"); -} - -void BeingHandler::processAlchemistRanksList(Net::MessageIn &msg) -{ - UNIMPLIMENTEDPACKET; - // +++ here need window with rank tables. - for (int f = 0; f < 10; f ++) - msg.readString(24, "name"); - for (int f = 0; f < 10; f ++) - msg.readInt32("points"); -} - -void BeingHandler::processTaekwonRanksList(Net::MessageIn &msg) -{ - UNIMPLIMENTEDPACKET; - // +++ here need window with rank tables. - for (int f = 0; f < 10; f ++) - msg.readString(24, "name"); - for (int f = 0; f < 10; f ++) - msg.readInt32("points"); -} - -void BeingHandler::processPkRanksList(Net::MessageIn &msg) -{ - UNIMPLIMENTEDPACKET; - // +++ here need window with rank tables. - for (int f = 0; f < 10; f ++) - msg.readString(24, "name"); - for (int f = 0; f < 10; f ++) - msg.readInt32("points"); -} - -void BeingHandler::processBeingChangeDirection(Net::MessageIn &msg) -{ - BLOCK_START("BeingHandler::processBeingChangeDirection") - if (!actorManager) - { - BLOCK_END("BeingHandler::processBeingChangeDirection") - return; - } - - Being *const dstBeing = actorManager->findBeing( - msg.readBeingId("being id")); - - msg.readInt16("head direction"); - - const uint8_t dir = Net::MessageIn::fromServerDirection( - static_cast<uint8_t>(msg.readUInt8("player direction") & 0x0FU)); - - if (!dstBeing) - { - BLOCK_END("BeingHandler::processBeingChangeDirection") - return; - } - - dstBeing->setDirection(dir); - if (localPlayer) - localPlayer->imitateDirection(dstBeing, dir); - BLOCK_END("BeingHandler::processBeingChangeDirection") -} - -void BeingHandler::processBeingSpecialEffect(Net::MessageIn &msg) -{ - if (!effectManager || !actorManager) - return; - - const BeingId id = msg.readBeingId("being id"); - Being *const being = actorManager->findBeing(id); - if (!being) - return; - - const int effectType = msg.readInt32("effect type"); - - if (Particle::enabled) - effectManager->trigger(effectType, being); - - // +++ need dehard code effectType == 3 - if (effectType == 3 && being->getType() == ActorType::Player - && socialWindow) - { // reset received damage - socialWindow->resetDamage(being->getName()); - } -} - -void BeingHandler::processBeingSpecialEffectNum(Net::MessageIn &msg) -{ - UNIMPLIMENTEDPACKET; - // +++ need somhow show this effects. - // type is not same with self/misc effect. - msg.readBeingId("account id"); - msg.readInt32("effect type"); - msg.readInt32("num"); // effect variable -} - -void BeingHandler::processBeingSoundEffect(Net::MessageIn &msg) -{ - UNIMPLIMENTEDPACKET; - // +++ need play this effect. - msg.readString(24, "sound effect name"); - msg.readUInt8("type"); - msg.readInt32("unused"); - msg.readInt32("source being id"); -} - void BeingHandler::viewPlayerEquipment(const Being *const being) { if (!being) @@ -1580,471 +452,4 @@ void BeingHandler::viewPlayerEquipment(const Being *const being) outMsg.writeBeingId(being->getId(), "account id"); } -void BeingHandler::processSkillGroundNoDamage(Net::MessageIn &msg) -{ - UNIMPLIMENTEDPACKET; - msg.readInt16("skill id"); - msg.readInt32("src id"); - msg.readInt16("val"); - msg.readInt16("x"); - msg.readInt16("y"); - msg.readInt32("tick"); -} - -void BeingHandler::processSkillEntry(Net::MessageIn &msg) -{ - UNIMPLIMENTEDPACKET; - msg.readInt16("len"); - msg.readInt32("accound id"); - msg.readInt32("creator accound id"); - msg.readInt16("x"); - msg.readInt16("y"); - msg.readInt32("job"); - msg.readUInt8("radius"); - msg.readUInt8("visible"); - msg.readUInt8("level"); -} - -void BeingHandler::processPlaterStatusChange(Net::MessageIn &msg) -{ - BLOCK_START("BeingHandler::processPlayerStop") - if (!actorManager) - { - BLOCK_END("BeingHandler::processPlayerStop") - return; - } - - // Change in players' flags - const BeingId id = msg.readBeingId("account id"); - Being *const dstBeing = actorManager->findBeing(id); - if (!dstBeing) - return; - - const uint16_t stunMode = msg.readInt16("stun mode"); - uint32_t statusEffects = msg.readInt16("status effect"); - statusEffects |= (static_cast<uint32_t>(msg.readInt32("opt?"))) << 16; - dstBeing->setKarma(msg.readUInt8("karma")); - - dstBeing->setStunMode(stunMode); - dstBeing->setStatusEffectBlock(0, static_cast<uint16_t>( - (statusEffects >> 16) & 0xffff)); - dstBeing->setStatusEffectBlock(16, static_cast<uint16_t>( - statusEffects & 0xffff)); - BLOCK_END("BeingHandler::processPlayerStop") -} - -void BeingHandler::processPlaterStatusChange2(Net::MessageIn &msg) -{ - if (!actorManager) - return; - - const BeingId id = msg.readBeingId("account id"); - Being *const dstBeing = actorManager->findBeing(id); - if (!dstBeing) - return; - - uint32_t statusEffects = msg.readInt32("status effect"); - dstBeing->setLevel(msg.readInt32("level")); - msg.readInt32("showEFST"); - - dstBeing->setStatusEffectBlock(0, static_cast<uint16_t>( - (statusEffects >> 16) & 0xffff)); - dstBeing->setStatusEffectBlock(16, static_cast<uint16_t>( - statusEffects & 0xffff)); -} - -void BeingHandler::processPlaterStatusChangeNoTick(Net::MessageIn &msg) -{ - const uint16_t status = msg.readInt16("index"); - const BeingId id = msg.readBeingId("account id"); - const Enable flag = fromBool(msg.readUInt8("state") - ? true : false, Enable); - - Being *const dstBeing = actorManager->findBeing(id); - if (!dstBeing) - return; - - dstBeing->setStatusEffect(status, flag); -} - -void BeingHandler::processBeingResurrect(Net::MessageIn &msg) -{ - BLOCK_START("BeingHandler::processBeingResurrect") - if (!actorManager || !localPlayer) - { - BLOCK_END("BeingHandler::processBeingResurrect") - return; - } - - // A being changed mortality status - - const BeingId id = msg.readBeingId("being id"); - msg.readInt16("unused"); - Being *const dstBeing = actorManager->findBeing(id); - if (!dstBeing) - { - BLOCK_END("BeingHandler::processBeingResurrect") - return; - } - - // If this is player's current target, clear it. - if (dstBeing == localPlayer->getTarget()) - localPlayer->stopAttack(); - - dstBeing->setAction(BeingAction::STAND, 0); - BLOCK_END("BeingHandler::processBeingResurrect") -} - -void BeingHandler::processPlayerGuilPartyInfo(Net::MessageIn &msg) -{ - BLOCK_START("BeingHandler::processPlayerGuilPartyInfo") - if (!actorManager) - { - BLOCK_END("BeingHandler::processPlayerGuilPartyInfo") - return; - } - - Being *const dstBeing = actorManager->findBeing( - msg.readBeingId("being id")); - - if (dstBeing) - { - dstBeing->setName(msg.readString(24, "char name")); - dstBeing->setPartyName(msg.readString(24, "party name")); - dstBeing->setGuildName(msg.readString(24, "guild name")); - dstBeing->setGuildPos(msg.readString(24, "guild pos")); - dstBeing->addToCache(); - } - BLOCK_END("BeingHandler::processPlayerGuilPartyInfo") -} - -void BeingHandler::processBeingRemoveSkil(Net::MessageIn &msg) -{ - UNIMPLIMENTEDPACKET; - // +++ if skill unit was added, here need remove it from actors - msg.readInt32("skill unit id"); -} - -void BeingHandler::processBeingFakeName(Net::MessageIn &msg) -{ - const BeingType::BeingType type = static_cast<BeingType::BeingType>( - msg.readUInt8("object type")); - const BeingId id = msg.readBeingId("npc id"); - msg.skip(8, "unused"); - const uint16_t job = msg.readInt16("class?"); // 111 - msg.skip(30, "unused"); - uint16_t x, y; - uint8_t dir; - msg.readCoordinates(x, y, dir, "position"); - msg.readUInt8("sx"); - msg.readUInt8("sy"); - msg.skip(4, "unsued"); - - Being *const dstBeing = createBeing2(msg, id, job, type); - if (!dstBeing) - return; - dstBeing->setSubtype(fromInt(job, BeingTypeId), 0); - dstBeing->setTileCoords(x, y); - dstBeing->setDirection(dir); -} - -void BeingHandler::processBeingStatUpdate1(Net::MessageIn &msg) -{ - const BeingId id = msg.readBeingId("account id"); - const int type = msg.readInt16("type"); - const int value = msg.readInt32("value"); - - Being *const dstBeing = actorManager->findBeing(id); - if (!dstBeing) - return; - - if (type != Ea::MANNER) - { - UNIMPLIMENTEDPACKET; - return; - } - dstBeing->setManner(value); -} - -void BeingHandler::processBeingSelfEffect(Net::MessageIn &msg) -{ - BLOCK_START("BeingHandler::processBeingSelfEffect") - if (!effectManager || !actorManager) - { - BLOCK_END("BeingHandler::processBeingSelfEffect") - return; - } - - const BeingId id = msg.readBeingId("being id"); - Being *const being = actorManager->findBeing(id); - if (!being) - { - BLOCK_END("BeingHandler::processBeingSelfEffect") - return; - } - - const int effectType = msg.readInt32("effect type"); - if (Particle::enabled) - effectManager->trigger(effectType, being); - - BLOCK_END("BeingHandler::processBeingSelfEffect") -} - -void BeingHandler::processMobInfo(Net::MessageIn &msg) -{ - const int len = msg.readInt16("len"); - if (len < 12) - return; - Being *const dstBeing = actorManager->findBeing( - msg.readBeingId("monster id")); - const int attackRange = msg.readInt32("range"); - if (dstBeing) - dstBeing->setAttackRange(attackRange); -} - -void BeingHandler::processBeingAttrs(Net::MessageIn &msg) -{ - const int len = msg.readInt16("len"); - if (len < 12) - return; - Being *const dstBeing = actorManager->findBeing( - msg.readBeingId("player id")); - const int gmLevel = msg.readInt32("gm level"); - if (dstBeing && gmLevel) - { - if (dstBeing == localPlayer) - localPlayer->setGMLevel(gmLevel); - dstBeing->setGM(true); - } - else - { - if (dstBeing == localPlayer) - localPlayer->setGMLevel(0); - if (dstBeing) - dstBeing->setGM(false); - } -} - -void BeingHandler::processMonsterInfo(Net::MessageIn &msg) -{ - UNIMPLIMENTEDPACKET; - - msg.readInt16("class"); - msg.readInt16("level"); - msg.readInt16("size"); - msg.readInt32("hp"); - msg.readInt16("def"); - msg.readInt16("race"); - msg.readInt16("mdef"); - msg.readInt16("ele"); -} - -void BeingHandler::processClassChange(Net::MessageIn &msg) -{ - UNIMPLIMENTEDPACKET; - - msg.readBeingId("being id"); - msg.readUInt8("type"); - msg.readInt32("class"); -} - -void BeingHandler::processSpiritBalls(Net::MessageIn &msg) -{ - UNIMPLIMENTEDPACKET; - - msg.readBeingId("being id"); - msg.readInt16("spirits amount"); -} - -void BeingHandler::processSpiritBallSingle(Net::MessageIn &msg) -{ - UNIMPLIMENTEDPACKET; - - msg.readBeingId("being id"); - msg.readInt16("spirits amount"); -} - -void BeingHandler::processBladeStop(Net::MessageIn &msg) -{ - UNIMPLIMENTEDPACKET; - - msg.readInt32("src being id"); - msg.readInt32("dst being id"); - msg.readInt32("flag"); -} - -void BeingHandler::processComboDelay(Net::MessageIn &msg) -{ - UNIMPLIMENTEDPACKET; - - msg.readBeingId("being id"); - msg.readInt32("wait"); -} - -void BeingHandler::processWddingEffect(Net::MessageIn &msg) -{ - UNIMPLIMENTEDPACKET; - - msg.readBeingId("being id"); -} - -void BeingHandler::processBeingSlide(Net::MessageIn &msg) -{ - UNIMPLIMENTEDPACKET; - - msg.readBeingId("being id"); - msg.readInt16("x"); - msg.readInt16("y"); -} - -void BeingHandler::processStarsKill(Net::MessageIn &msg) -{ - UNIMPLIMENTEDPACKET; - - msg.readString(24, "map name"); - msg.readInt32("monster id"); - msg.readUInt8("start"); - msg.readUInt8("result"); -} - -void BeingHandler::processGladiatorFeelRequest(Net::MessageIn &msg) -{ - UNIMPLIMENTEDPACKET; - - msg.readUInt8("which"); -} - -void BeingHandler::processBossMapInfo(Net::MessageIn &msg) -{ - UNIMPLIMENTEDPACKET; - - msg.readUInt8("info type"); - msg.readInt32("x"); - msg.readInt32("y"); - msg.readInt16("min hours"); - msg.readInt16("min minutes"); - msg.readInt16("max hours"); - msg.readInt16("max minutes"); - msg.readString(24, "monster name"); // really can be used 51 byte? -} - -void BeingHandler::processBeingFont(Net::MessageIn &msg) -{ - UNIMPLIMENTEDPACKET; - - msg.readBeingId("account id"); - msg.readInt16("font"); -} - -void BeingHandler::processBeingMilleniumShield(Net::MessageIn &msg) -{ - UNIMPLIMENTEDPACKET; - - msg.readBeingId("account id"); - msg.readInt16("shields"); - msg.readInt16("unused"); -} - -void BeingHandler::processBeingCharm(Net::MessageIn &msg) -{ - UNIMPLIMENTEDPACKET; - - msg.readBeingId("account id"); - msg.readInt16("charm type"); - msg.readInt16("charm count"); -} - -void BeingHandler::processBeingViewEquipment(Net::MessageIn &msg) -{ - UNIMPLIMENTEDPACKET; - - const int count = (msg.readInt16("len") - 45) / 31; - msg.readString(24, "name"); - msg.readInt16("job"); - msg.readInt16("head"); - msg.readInt16("accessory"); - msg.readInt16("accessory2"); - msg.readInt16("accessory3"); - msg.readInt16("robe"); - msg.readInt16("hair color"); - msg.readInt16("body color"); - msg.readUInt8("gender"); - for (int f = 0; f < count; f ++) - { - msg.readInt16("index"); - msg.readInt16("item id"); - msg.readUInt8("item type"); - msg.readInt32("location"); - msg.readInt32("wear state"); - msg.readInt8("refine"); - for (int d = 0; d < 4; d ++) - msg.readInt16("card"); - msg.readInt32("hire expire date (?)"); - msg.readInt16("equip type"); - msg.readInt16("item sprite number"); - msg.readUInt8("flags"); - } -} - -void BeingHandler::processPvpSet(Net::MessageIn &msg) -{ - BLOCK_START("BeingHandler::processPvpSet") - const BeingId id = msg.readBeingId("being id"); - const int rank = msg.readInt32("rank"); - msg.readInt32("num"); - if (actorManager) - { - Being *const dstBeing = actorManager->findBeing(id); - if (dstBeing) - dstBeing->setPvpRank(rank); - } - BLOCK_END("BeingHandler::processPvpSet") -} - -void BeingHandler::processNameResponse2(Net::MessageIn &msg) -{ - BLOCK_START("BeingHandler::processNameResponse2") - if (!actorManager || !localPlayer) - { - BLOCK_END("BeingHandler::processNameResponse2") - return; - } - - const int len = msg.readInt16("len"); - const BeingId beingId = msg.readBeingId("account ic"); - const std::string str = msg.readString(len - 8, "name"); - Being *const dstBeing = actorManager->findBeing(beingId); - if (dstBeing) - { - if (beingId == localPlayer->getId()) - { - localPlayer->pingResponse(); - } - else - { - dstBeing->setName(str); - dstBeing->updateGuild(); - dstBeing->addToCache(); - - if (dstBeing->getType() == ActorType::Player) - dstBeing->updateColors(); - - if (localPlayer) - { - const Party *const party = localPlayer->getParty(); - if (party && party->isMember(dstBeing->getId())) - { - PartyMember *const member = party->getMember( - dstBeing->getId()); - - if (member) - member->setName(dstBeing->getName()); - } - localPlayer->checkNewName(dstBeing); - } - } - } - BLOCK_END("BeingHandler::processNameResponse2") -} - } // namespace EAthena diff --git a/src/net/eathena/beinghandler.h b/src/net/eathena/beinghandler.h index 7dda4f2cb..1a439c807 100644 --- a/src/net/eathena/beinghandler.h +++ b/src/net/eathena/beinghandler.h @@ -47,126 +47,7 @@ class BeingHandler final : public MessageHandler, public Ea::BeingHandler void requestRanks(const RankT rank) const override final; protected: - static Being *createBeing2(Net::MessageIn &msg, - const BeingId id, - const int16_t job, - const BeingType::BeingType beingType); - - static void processBeingChangeLook2(Net::MessageIn &msg); - - static void processBeingChangeLookCards(Net::MessageIn &msg); - - static void processBeingVisible(Net::MessageIn &msg); - - static void processBeingMove(Net::MessageIn &msg); - - static void processBeingSpawn(Net::MessageIn &msg); - - static void processMapTypeProperty(Net::MessageIn &msg); - - static void processMapType(Net::MessageIn &msg); - - static void processSkillCasting(Net::MessageIn &msg); - - static void processBeingStatusChange(Net::MessageIn &msg); - - static void processBeingStatusChange2(Net::MessageIn &msg); - - static void processBeingMove2(Net::MessageIn &msg); - - static void processBeingAction2(Net::MessageIn &msg); - - static void processMonsterHp(Net::MessageIn &msg); - - static void processSkillAutoCast(Net::MessageIn &msg); - - static void processRanksList(Net::MessageIn &msg); - - static void processBeingChangeDirection(Net::MessageIn &msg); - - static void processBeingChangeLookContinue(Net::MessageIn &msg, - Being *const dstBeing, - const uint8_t type, - const int id, - const int id2, - const int *cards) - A_NONNULL(2); - - static void processBeingSpecialEffect(Net::MessageIn &msg); - - static void processBeingSpecialEffectNum(Net::MessageIn &msg); - - static void processBeingSoundEffect(Net::MessageIn &msg); - void viewPlayerEquipment(const Being *const being); - - static void processSkillGroundNoDamage(Net::MessageIn &msg); - - static void processSkillEntry(Net::MessageIn &msg); - - static void processPlaterStatusChange(Net::MessageIn &msg); - - static void processPlaterStatusChangeNoTick(Net::MessageIn &msg); - - static void processBeingResurrect(Net::MessageIn &msg); - - static void processPlayerGuilPartyInfo(Net::MessageIn &msg); - - static void processBeingRemoveSkil(Net::MessageIn &msg); - - static void processBeingFakeName(Net::MessageIn &msg); - - static void processBeingStatUpdate1(Net::MessageIn &msg); - - static void processPlaterStatusChange2(Net::MessageIn &msg); - - static void processBeingSelfEffect(Net::MessageIn &msg); - - static void processMobInfo(Net::MessageIn &msg); - - static void processBeingAttrs(Net::MessageIn &msg); - - static void processMonsterInfo(Net::MessageIn &msg); - - static void processClassChange(Net::MessageIn &msg); - - static void processSpiritBalls(Net::MessageIn &msg); - - static void processSpiritBallSingle(Net::MessageIn &msg); - - static void processBladeStop(Net::MessageIn &msg); - - static void processComboDelay(Net::MessageIn &msg); - - static void processWddingEffect(Net::MessageIn &msg); - - static void processBeingSlide(Net::MessageIn &msg); - - static void processStarsKill(Net::MessageIn &msg); - - static void processBlacksmithRanksList(Net::MessageIn &msg); - - static void processAlchemistRanksList(Net::MessageIn &msg); - - static void processTaekwonRanksList(Net::MessageIn &msg); - - static void processPkRanksList(Net::MessageIn &msg); - - static void processGladiatorFeelRequest(Net::MessageIn &msg); - - static void processBossMapInfo(Net::MessageIn &msg); - - static void processBeingFont(Net::MessageIn &msg); - - static void processBeingMilleniumShield(Net::MessageIn &msg); - - static void processBeingCharm(Net::MessageIn &msg); - - static void processBeingViewEquipment(Net::MessageIn &msg); - - static void processPvpSet(Net::MessageIn &msg); - - static void processNameResponse2(Net::MessageIn &msg); }; } // namespace EAthena diff --git a/src/net/eathena/beingnet.cpp b/src/net/eathena/beingnet.cpp new file mode 100644 index 000000000..1da2ddad0 --- /dev/null +++ b/src/net/eathena/beingnet.cpp @@ -0,0 +1,1670 @@ +/* + * The ManaPlus Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011-2015 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "net/eathena/beingnet.h" + +#include "actormanager.h" +#include "effectmanager.h" +#include "game.h" +#include "itemcolormanager.h" +#include "notifymanager.h" +#include "party.h" + +#include "being/mercenaryinfo.h" + +#include "enums/resources/notifytypes.h" + +#include "particle/particle.h" + +#include "input/keyboardconfig.h" + +#include "gui/windows/skilldialog.h" +#include "gui/windows/socialwindow.h" +#include "gui/windows/outfitwindow.h" + +#include "net/serverfeatures.h" + +#include "net/ea/beingnet.h" +#include "net/ea/eaprotocol.h" + +#include "net/charserverhandler.h" + +#include "net/character.h" + +#include "net/eathena/maptypeproperty2.h" +#include "net/eathena/messageout.h" +#include "net/eathena/protocol.h" +#include "net/eathena/sprite.h" + +#include "resources/iteminfo.h" +#include "resources/db/itemdb.h" + +#include "resources/map/map.h" + +#include "utils/timer.h" + +#include "debug.h" + +namespace EAthena +{ + +void BeingNet::processBeingChangeLook2(Net::MessageIn &msg) +{ + if (!actorManager) + return; + + Being *const dstBeing = actorManager->findBeing( + msg.readBeingId("being id")); + const uint8_t type = msg.readUInt8("type"); + + const int id = msg.readInt16("id1"); + unsigned int id2 = msg.readInt16("id2"); + if (type != 2) + id2 = 1; + + if (!localPlayer || !dstBeing) + return; + + processBeingChangeLookContinue(msg, dstBeing, type, id, id2, nullptr); +} + +void BeingNet::processBeingChangeLookCards(Net::MessageIn &msg) +{ + Being *dstBeing = nullptr; + int cards[4]; + + if (!actorManager) + { // here can be look from char server + if (!serverFeatures->haveAdvancedSprites()) + return; + Net::Characters &chars = Net::CharServerHandler::mCharacters; + const BeingId id = msg.readBeingId("being id"); + + FOR_EACH (Net::Characters::iterator, it, chars) + { + Net::Character *character = *it; + if (character->dummy && character->dummy->getId() == id) + { + dstBeing = character->dummy; + break; + } + } + } + else + { + dstBeing = actorManager->findBeing( + msg.readBeingId("being id")); + } + + const uint8_t type = msg.readUInt8("type"); + + const int id = msg.readInt16("id1"); + unsigned int id2 = msg.readInt16("id2"); + if (type != 2) + id2 = 1; + + for (int f = 0; f < 4; f ++) + cards[f] = msg.readInt16("card"); + + if (!dstBeing) + return; + + processBeingChangeLookContinue(msg, dstBeing, type, id, id2, &cards[0]); +} + +void BeingNet::processBeingChangeLookContinue(Net::MessageIn &msg, + Being *const dstBeing, + const uint8_t type, + const int id, + const int id2, + const int *cards A_UNUSED) +{ + if (dstBeing->getType() == ActorType::Player) + dstBeing->setOtherTime(); + + const ItemColor itemColor = ItemColorManager::getColorFromCards(cards); + const std::string color; + switch (type) + { + case 0: // change race + dstBeing->setSubtype(fromInt(id, BeingTypeId), + dstBeing->getLook()); + break; + case 1: // eAthena LOOK_HAIR + dstBeing->setHairColor(fromInt(id, ItemColor)); + dstBeing->setSpriteID(SPRITE_HAIR_COLOR, id * -1); + break; + case 2: // LOOK_WEAPON Weapon ID in id, Shield ID in id2 + dstBeing->setSprite(SPRITE_BODY, id, "", itemColor, true); + dstBeing->setSprite(SPRITE_FLOOR, id2); + if (localPlayer) + localPlayer->imitateOutfit(dstBeing, SPRITE_FLOOR); + break; + case 3: // LOOK_HEAD_BOTTOM + dstBeing->setSprite(SPRITE_WEAPON, id, color, itemColor); + if (localPlayer) + localPlayer->imitateOutfit(dstBeing, SPRITE_WEAPON); + break; + case 4: // LOOK_HEAD_TOP Change upper headgear for eAthena, hat for us + dstBeing->setSprite(SPRITE_CLOTHES_COLOR, id, color, itemColor); + if (localPlayer) + localPlayer->imitateOutfit(dstBeing, SPRITE_CLOTHES_COLOR); + break; + case 5: // LOOK_HEAD_MID Change middle headgear for eathena, + // armor for us + dstBeing->setSprite(SPRITE_HEAD_BOTTOM, id, color, itemColor); + if (localPlayer) + localPlayer->imitateOutfit(dstBeing, SPRITE_HEAD_BOTTOM); + break; + case 6: // eAthena LOOK_HAIR_COLOR + dstBeing->setHairColor(fromInt(id, ItemColor)); + dstBeing->setSpriteColor(SPRITE_HAIR_COLOR, + ItemDB::get(dstBeing->getSpriteID( + SPRITE_HAIR_COLOR)).getDyeColorsString( + fromInt(id, ItemColor))); + break; + case 7: // Clothes color. Now used as look + dstBeing->setLook(static_cast<uint8_t>(id)); + break; + case 8: // eAthena LOOK_SHIELD + dstBeing->setSprite(SPRITE_FLOOR, id, color, itemColor); + if (localPlayer) + localPlayer->imitateOutfit(dstBeing, SPRITE_FLOOR); + break; + case 9: // eAthena LOOK_SHOES + dstBeing->setSprite(SPRITE_HAIR, id, color, itemColor); + if (localPlayer) + localPlayer->imitateOutfit(dstBeing, SPRITE_HAIR); + break; + case 10: // LOOK_GLOVES + dstBeing->setSprite(SPRITE_SHOES, id, color, itemColor); + if (localPlayer) + localPlayer->imitateOutfit(dstBeing, SPRITE_SHOES); + break; + case 11: // LOOK_FLOOR + dstBeing->setSprite(SPRITE_SHIELD, id, color, itemColor); + if (localPlayer) + localPlayer->imitateOutfit(dstBeing, SPRITE_SHIELD); + break; + case 12: // LOOK_ROBE + dstBeing->setSprite(SPRITE_HEAD_TOP, id, color, itemColor); + if (localPlayer) + localPlayer->imitateOutfit(dstBeing, SPRITE_HEAD_TOP); + break; + case 13: // COSTUME_HEAD_TOP + dstBeing->setSprite(SPRITE_HEAD_MID, id, color, itemColor); + if (localPlayer) + localPlayer->imitateOutfit(dstBeing, SPRITE_HEAD_MID); + break; + case 14: // COSTUME_HEAD_MID + dstBeing->setSprite(SPRITE_ROBE, id, color, itemColor); + if (localPlayer) + localPlayer->imitateOutfit(dstBeing, SPRITE_ROBE); + break; + case 15: // COSTUME_HEAD_LOW + dstBeing->setSprite(SPRITE_EVOL2, id, color, itemColor); + if (localPlayer) + localPlayer->imitateOutfit(dstBeing, SPRITE_EVOL2); + break; + case 16: // COSTUME_GARMENT + dstBeing->setSprite(SPRITE_EVOL3, id, color, itemColor); + if (localPlayer) + localPlayer->imitateOutfit(dstBeing, SPRITE_EVOL3); + break; + case 17: // ARMOR + dstBeing->setSprite(SPRITE_EVOL4, id, color, itemColor); + if (localPlayer) + localPlayer->imitateOutfit(dstBeing, SPRITE_EVOL4); + break; + default: + UNIMPLIMENTEDPACKET; + break; + } +} + +void BeingNet::processBeingVisible(Net::MessageIn &msg) +{ + if (!actorManager) + return; + + msg.readInt16("len"); + const BeingType::BeingType type = static_cast<BeingType::BeingType>( + msg.readUInt8("object type")); + + // Information about a being in range + const BeingId id = msg.readBeingId("being id"); + BeingId spawnId; + if (id == Ea::BeingNet::mSpawnId) + spawnId = Ea::BeingNet::mSpawnId; + else + spawnId = BeingId_zero; + Ea::BeingNet::mSpawnId = BeingId_zero; + + int16_t speed = msg.readInt16("speed"); + const uint16_t stunMode = msg.readInt16("opt1"); + // probably wrong effect usage + const uint32_t statusEffects = msg.readInt16("opt2"); + msg.readInt32("option"); + + const int16_t job = msg.readInt16("class"); + + Being *dstBeing = actorManager->findBeing(id); + + if (dstBeing && dstBeing->getType() == ActorType::Monster + && !dstBeing->isAlive()) + { + actorManager->destroy(dstBeing); + actorManager->erase(dstBeing); + dstBeing = nullptr; + } + + if (!dstBeing) + { + if (actorManager->isBlocked(id) == true) + return; + + dstBeing = createBeing2(msg, id, job, type); + if (!dstBeing) + return; + } + else + { + // undeleting marked for deletion being + if (dstBeing->getType() == ActorType::Npc) + actorManager->undelete(dstBeing); + } + + if (dstBeing->getType() == ActorType::Player) + dstBeing->setMoveTime(); + + if (spawnId != BeingId_zero) + { + dstBeing->setAction(BeingAction::SPAWN, 0); + } + else + { + dstBeing->clearPath(); + dstBeing->setActionTime(tick_time); + dstBeing->setAction(BeingAction::STAND, 0); + } + + // Prevent division by 0 when calculating frame + if (speed == 0) + speed = 150; + + dstBeing->setWalkSpeed(Vector(speed, speed, 0)); + dstBeing->setSubtype(fromInt(job, BeingTypeId), 0); + if (dstBeing->getType() == ActorType::Monster && localPlayer) + localPlayer->checkNewName(dstBeing); + + const int hairStyle = msg.readInt16("hair style"); + const uint32_t weapon = static_cast<uint32_t>(msg.readInt32("weapon")); + const uint16_t headBottom = msg.readInt16("head bottom"); + + const uint16_t headTop = msg.readInt16("head top"); + const uint16_t headMid = msg.readInt16("head mid"); + const ItemColor hairColor = fromInt(msg.readInt16("hair color"), ItemColor); + const uint16_t shoes = msg.readInt16("shoes or clothes color?"); + + const uint16_t gloves = msg.readInt16("head dir / gloves"); + // may be use robe as gloves? + msg.readInt16("robe"); + msg.readInt32("guild id"); + msg.readInt16("guild emblem"); + dstBeing->setManner(msg.readInt16("manner")); + dstBeing->setStatusEffectBlock(32, static_cast<uint16_t>( + msg.readInt32("opt3"))); + dstBeing->setKarma(msg.readUInt8("karma")); + uint8_t gender = static_cast<uint8_t>(msg.readUInt8("gender") & 3); + + if (dstBeing->getType() == ActorType::Player) + { + dstBeing->setGender(Being::intToGender(gender)); + dstBeing->setHairColor(hairColor); + // Set these after the gender, as the sprites may be gender-specific + Ea::BeingNet::setSprite(dstBeing, SPRITE_HAIR_COLOR, hairStyle * -1, + ItemDB::get(-hairStyle).getDyeColorsString(hairColor)); + Ea::BeingNet::setSprite(dstBeing, SPRITE_WEAPON, headBottom); + Ea::BeingNet::setSprite(dstBeing, SPRITE_HEAD_BOTTOM, headMid); + Ea::BeingNet::setSprite(dstBeing, SPRITE_CLOTHES_COLOR, headTop); + Ea::BeingNet::setSprite(dstBeing, SPRITE_HAIR, shoes); + Ea::BeingNet::setSprite(dstBeing, SPRITE_SHOES, gloves); + Ea::BeingNet::setSprite(dstBeing, SPRITE_BODY, weapon, "", + ItemColor_one, true); +// Ea::BeingNet::setSprite(dstBeing, SPRITE_FLOOR, shield); + } + else if (dstBeing->getType() == ActorType::Npc + && serverFeatures->haveNpcGender()) + { + dstBeing->setGender(Being::intToGender(gender)); + } + + uint8_t dir; + uint16_t x, y; + msg.readCoordinates(x, y, dir, "position"); + msg.readInt8("xs"); + msg.readInt8("ys"); + msg.readUInt8("action type"); + dstBeing->setTileCoords(x, y); + + if (job == 45 && socialWindow && outfitWindow) + { + const int num = socialWindow->getPortalIndex(x, y); + if (num >= 0) + { + dstBeing->setName(keyboard.getKeyShortString( + outfitWindow->keyName(num))); + } + else + { + dstBeing->setName(""); + } + } + + dstBeing->setDirection(dir); + + const int level = static_cast<int>(msg.readInt16("level")); + if (level) + dstBeing->setLevel(level); + msg.readInt16("font"); + + const int maxHP = msg.readInt32("max hp"); + const int hp = msg.readInt32("hp"); + dstBeing->setMaxHP(maxHP); + dstBeing->setHP(hp); + + msg.readInt8("is boss"); + + dstBeing->setStunMode(stunMode); + dstBeing->setStatusEffectBlock(0, static_cast<uint16_t>( + (statusEffects >> 16) & 0xffffU)); + dstBeing->setStatusEffectBlock(16, static_cast<uint16_t>( + statusEffects & 0xffffU)); +} + +void BeingNet::processBeingMove(Net::MessageIn &msg) +{ + if (!actorManager) + return; + + msg.readInt16("len"); + const BeingType::BeingType type = static_cast<BeingType::BeingType>( + msg.readUInt8("object type")); + + // Information about a being in range + const BeingId id = msg.readBeingId("being id"); + BeingId spawnId; + if (id == Ea::BeingNet::mSpawnId) + spawnId = Ea::BeingNet::mSpawnId; + else + spawnId = BeingId_zero; + Ea::BeingNet::mSpawnId = BeingId_zero; + int16_t speed = msg.readInt16("speed"); +// if (visible) +// { + const uint16_t stunMode = msg.readInt16("opt1"); + // probably wrong effect usage + const uint32_t statusEffects = msg.readInt16("opt2"); +// } +// else +// { +// commented for now, probably it can be removed after testing +// msg.readInt16("body state"); +// msg.readInt16("health state"); +// } + msg.readInt32("effect state"); + + const int16_t job = msg.readInt16("class"); + + Being *dstBeing = actorManager->findBeing(id); + + if (dstBeing && dstBeing->getType() == ActorType::Monster + && !dstBeing->isAlive()) + { + actorManager->destroy(dstBeing); + actorManager->erase(dstBeing); + dstBeing = nullptr; + } + + if (!dstBeing) + { + if (actorManager->isBlocked(id) == true) + return; + + dstBeing = createBeing2(msg, id, job, type); + if (!dstBeing) + return; + } + else + { + // undeleting marked for deletion being + if (dstBeing->getType() == ActorType::Npc) + actorManager->undelete(dstBeing); + } + + if (dstBeing->getType() == ActorType::Player) + dstBeing->setMoveTime(); + + if (spawnId != BeingId_zero) + dstBeing->setAction(BeingAction::SPAWN, 0); + + // Prevent division by 0 when calculating frame + if (speed == 0) + speed = 150; + + dstBeing->setWalkSpeed(Vector(speed, speed, 0)); + dstBeing->setSubtype(fromInt(job, BeingTypeId), 0); + if (dstBeing->getType() == ActorType::Monster && localPlayer) + localPlayer->checkNewName(dstBeing); + + const int hairStyle = msg.readInt16("hair style"); + const uint32_t weapon = static_cast<uint32_t>(msg.readInt32("weapon")); + const uint16_t headBottom = msg.readInt16("head bottom"); + + msg.readInt32("tick"); + + const uint16_t headTop = msg.readInt16("head top"); + const uint16_t headMid = msg.readInt16("head mid"); + const ItemColor hairColor = fromInt( + msg.readInt16("hair color"), ItemColor); + const uint16_t shoes = msg.readInt16("shoes or clothes color?"); + + const uint16_t gloves = msg.readInt16("head dir / gloves"); + // may be use robe as gloves? + msg.readInt16("robe"); + msg.readInt32("guild id"); + msg.readInt16("guild emblem"); + dstBeing->setManner(msg.readInt16("manner")); + dstBeing->setStatusEffectBlock(32, static_cast<uint16_t>( + msg.readInt32("opt3"))); + dstBeing->setKarma(msg.readUInt8("karma")); + uint8_t gender = static_cast<uint8_t>(msg.readUInt8("gender") & 3); + + if (dstBeing->getType() == ActorType::Player) + { + dstBeing->setGender(Being::intToGender(gender)); + dstBeing->setHairColor(hairColor); + // Set these after the gender, as the sprites may be gender-specific + Ea::BeingNet::setSprite(dstBeing, SPRITE_HAIR_COLOR, hairStyle * -1, + ItemDB::get(-hairStyle).getDyeColorsString(hairColor)); + if (!serverFeatures->haveMove3()) + { + Ea::BeingNet::setSprite(dstBeing, SPRITE_WEAPON, headBottom); + Ea::BeingNet::setSprite(dstBeing, SPRITE_HEAD_BOTTOM, headMid); + Ea::BeingNet::setSprite(dstBeing, SPRITE_CLOTHES_COLOR, headTop); + Ea::BeingNet::setSprite(dstBeing, SPRITE_HAIR, shoes); + Ea::BeingNet::setSprite(dstBeing, SPRITE_SHOES, gloves); + Ea::BeingNet::setSprite(dstBeing, SPRITE_BODY, weapon, "", + ItemColor_one, true); + } +// Ea::BeingNet::setSprite(dstBeing, SPRITE_FLOOR, shield); + } + else if (dstBeing->getType() == ActorType::Npc + && serverFeatures->haveNpcGender()) + { + dstBeing->setGender(Being::intToGender(gender)); + } + + uint16_t srcX, srcY, dstX, dstY; + msg.readCoordinatePair(srcX, srcY, dstX, dstY, "move path"); + msg.readUInt8("(sx<<4) | (sy&0x0f)"); + msg.readInt8("xs"); + msg.readInt8("ys"); + dstBeing->setAction(BeingAction::STAND, 0); + dstBeing->setTileCoords(srcX, srcY); + if (localPlayer) + localPlayer->followMoveTo(dstBeing, srcX, srcY, dstX, dstY); + if (!serverFeatures->haveMove3()) + dstBeing->setDestination(dstX, dstY); + + // because server don't send direction in move packet, we fixing it + + uint8_t d = 0; + if (srcX == dstX && srcY == dstY) + { // if player did one step from invisible area to visible, + // move path is broken + int x2 = localPlayer->getTileX(); + int y2 = localPlayer->getTileY(); + if (abs(x2 - srcX) > abs(y2 - srcY)) + y2 = srcY; + else + x2 = srcX; + d = dstBeing->calcDirection(x2, y2); + } + else + { + d = dstBeing->calcDirection(dstX, dstY); + } + if (d && dstBeing->getDirection() != d) + dstBeing->setDirection(d); + + const int level = static_cast<int>(msg.readInt16("level")); + if (level) + dstBeing->setLevel(level); + msg.readInt16("font"); + + const int maxHP = msg.readInt32("max hp"); + const int hp = msg.readInt32("hp"); + dstBeing->setMaxHP(maxHP); + dstBeing->setHP(hp); + + msg.readInt8("is boss"); + + dstBeing->setStunMode(stunMode); + dstBeing->setStatusEffectBlock(0, static_cast<uint16_t>( + (statusEffects >> 16) & 0xffffU)); + dstBeing->setStatusEffectBlock(16, static_cast<uint16_t>( + statusEffects & 0xffffU)); +} + +void BeingNet::processBeingSpawn(Net::MessageIn &msg) +{ + if (!actorManager) + return; + + msg.readInt16("len"); + const BeingType::BeingType type = static_cast<BeingType::BeingType>( + msg.readUInt8("object type")); + + // Information about a being in range + const BeingId id = msg.readBeingId("being id"); + Ea::BeingNet::mSpawnId = id; + const BeingId spawnId = id; + int16_t speed = msg.readInt16("speed"); +// if (visible) +// { + const uint16_t stunMode = msg.readInt16("opt1"); + // probably wrong effect usage + const uint32_t statusEffects = msg.readInt16("opt2"); +// } +// else +// { +// commented for now, probably it can be removed after testing +// msg.readInt16("body state"); +// msg.readInt16("health state"); +// } + msg.readInt32("effect state"); + + const int16_t job = msg.readInt16("class"); + + Being *dstBeing = actorManager->findBeing(id); + + if (dstBeing && dstBeing->getType() == ActorType::Monster + && !dstBeing->isAlive()) + { + actorManager->destroy(dstBeing); + actorManager->erase(dstBeing); + dstBeing = nullptr; + } + + if (!dstBeing) + { + if (actorManager->isBlocked(id) == true) + return; + + dstBeing = createBeing2(msg, id, job, type); + if (!dstBeing) + return; + } + else + { + // undeleting marked for deletion being + if (dstBeing->getType() == ActorType::Npc) + actorManager->undelete(dstBeing); + } + + if (dstBeing->getType() == ActorType::Player) + dstBeing->setMoveTime(); + + if (spawnId != BeingId_zero) + dstBeing->setAction(BeingAction::SPAWN, 0); + + // Prevent division by 0 when calculating frame + if (speed == 0) + speed = 150; + + dstBeing->setWalkSpeed(Vector(speed, speed, 0)); + dstBeing->setSubtype(fromInt(job, BeingTypeId), 0); + if (dstBeing->getType() == ActorType::Monster && localPlayer) + localPlayer->checkNewName(dstBeing); + + const int hairStyle = msg.readInt16("hair style"); + const uint32_t weapon = static_cast<uint32_t>(msg.readInt32("weapon")); + const uint16_t headBottom = msg.readInt16("head bottom"); + + const uint16_t headTop = msg.readInt16("head top"); + const uint16_t headMid = msg.readInt16("head mid"); + const ItemColor hairColor = fromInt( + msg.readInt16("hair color"), ItemColor); + const uint16_t shoes = msg.readInt16("shoes or clothes color?"); + const uint16_t gloves = msg.readInt16("head dir / gloves"); + // may be use robe as gloves? + msg.readInt16("robe"); + msg.readInt32("guild id"); + msg.readInt16("guild emblem"); + dstBeing->setManner(msg.readInt16("manner")); + dstBeing->setStatusEffectBlock(32, static_cast<uint16_t>( + msg.readInt32("opt3"))); + dstBeing->setKarma(msg.readUInt8("karma")); + uint8_t gender = static_cast<uint8_t>(msg.readUInt8("gender") & 3); + + if (dstBeing->getType() == ActorType::Player) + { + dstBeing->setGender(Being::intToGender(gender)); + dstBeing->setHairColor(hairColor); + // Set these after the gender, as the sprites may be gender-specific + Ea::BeingNet::setSprite(dstBeing, SPRITE_HAIR_COLOR, hairStyle * -1, + ItemDB::get(-hairStyle).getDyeColorsString(hairColor)); + Ea::BeingNet::setSprite(dstBeing, SPRITE_WEAPON, headBottom); + Ea::BeingNet::setSprite(dstBeing, SPRITE_HEAD_BOTTOM, headMid); + Ea::BeingNet::setSprite(dstBeing, SPRITE_CLOTHES_COLOR, headTop); + Ea::BeingNet::setSprite(dstBeing, SPRITE_HAIR, shoes); + Ea::BeingNet::setSprite(dstBeing, SPRITE_SHOES, gloves); + Ea::BeingNet::setSprite(dstBeing, SPRITE_BODY, weapon, "", + ItemColor_one, true); +// Ea::BeingNet::setSprite(dstBeing, SPRITE_FLOOR, shield); + } + else if (dstBeing->getType() == ActorType::Npc + && serverFeatures->haveNpcGender()) + { + dstBeing->setGender(Being::intToGender(gender)); + } + + uint8_t dir; + uint16_t x, y; + msg.readCoordinates(x, y, dir, "position"); + msg.readInt8("xs"); + msg.readInt8("ys"); + dstBeing->setTileCoords(x, y); + + if (job == 45 && socialWindow && outfitWindow) + { + const int num = socialWindow->getPortalIndex(x, y); + if (num >= 0) + { + dstBeing->setName(keyboard.getKeyShortString( + outfitWindow->keyName(num))); + } + else + { + dstBeing->setName(""); + } + } + + dstBeing->setDirection(dir); + + const int level = static_cast<int>(msg.readInt16("level")); + if (level) + dstBeing->setLevel(level); + msg.readInt16("font"); + + const int maxHP = msg.readInt32("max hp"); + const int hp = msg.readInt32("hp"); + dstBeing->setMaxHP(maxHP); + dstBeing->setHP(hp); + + msg.readInt8("is boss"); + + dstBeing->setStunMode(stunMode); + dstBeing->setStatusEffectBlock(0, static_cast<uint16_t>( + (statusEffects >> 16) & 0xffffU)); + dstBeing->setStatusEffectBlock(16, static_cast<uint16_t>( + statusEffects & 0xffffU)); +} + +void BeingNet::processMapTypeProperty(Net::MessageIn &msg) +{ + const int16_t type = msg.readInt16("type"); + const int flags = msg.readInt32("flags"); + if (type == 0x28) + { + // +++ need get other flags from here + MapTypeProperty2 props; + props.data = static_cast<uint32_t>(flags); + Game *const game = Game::instance(); + if (!game) + return; + Map *const map = game->getCurrentMap(); + if (!map) + return; + map->setPvpMode(props.bits.party | (props.bits.guild * 2)); + } +} + +void BeingNet::processMapType(Net::MessageIn &msg) +{ + const int16_t type = msg.readInt16("type"); + if (type == 19) + NotifyManager::notify(NotifyTypes::MAP_TYPE_BATTLEFIELD); + else + UNIMPLIMENTEDPACKET; +} + +void BeingNet::processSkillCasting(Net::MessageIn &msg) +{ + // +++ need use other parameters + + const BeingId srcId = msg.readBeingId("src id"); + const BeingId dstId = msg.readBeingId("dst id"); + const int dstX = msg.readInt16("dst x"); + const int dstY = msg.readInt16("dst y"); + const int skillId = msg.readInt16("skill id"); + msg.readInt32("property"); // can be used to trigger effect + msg.readInt32("cast time"); + msg.readInt8("dispossable"); + + if (!effectManager) + return; + + if (srcId == BeingId_zero) + { + UNIMPLIMENTEDPACKET; + return; + } + else if (dstId != BeingId_zero) + { // being to being + Being *const srcBeing = actorManager->findBeing(srcId); + Being *const dstBeing = actorManager->findBeing(dstId); + skillDialog->playCastingSrcEffect(skillId, srcBeing); + skillDialog->playCastingDstEffect(skillId, dstBeing); + } + else if (dstX != 0 || dstY != 0) + { // being to position + UNIMPLIMENTEDPACKET; + } +} + +void BeingNet::processBeingStatusChange(Net::MessageIn &msg) +{ + BLOCK_START("BeingNet::processBeingStatusChange") + if (!actorManager) + { + BLOCK_END("BeingNet::processBeingStatusChange") + return; + } + + // Status change + const uint16_t status = msg.readInt16("status"); + const BeingId id = msg.readBeingId("being id"); + const Enable flag = fromBool( + msg.readUInt8("flag: 0: stop, 1: start"), Enable); + msg.readInt32("total"); + msg.readInt32("left"); + msg.readInt32("val1"); + msg.readInt32("val2"); + msg.readInt32("val3"); + + Being *const dstBeing = actorManager->findBeing(id); + if (dstBeing) + dstBeing->setStatusEffect(status, flag); + BLOCK_END("BeingNet::processBeingStatusChange") +} + +void BeingNet::processBeingStatusChange2(Net::MessageIn &msg) +{ + BLOCK_START("BeingNet::processBeingStatusChange") + if (!actorManager) + { + BLOCK_END("BeingNet::processBeingStatusChange") + return; + } + + // Status change + const uint16_t status = msg.readInt16("status"); + const BeingId id = msg.readBeingId("being id"); + const Enable flag = fromBool( + msg.readUInt8("flag: 0: stop, 1: start"), Enable); + msg.readInt32("left"); + msg.readInt32("val1"); + msg.readInt32("val2"); + msg.readInt32("val3"); + + Being *const dstBeing = actorManager->findBeing(id); + if (dstBeing) + dstBeing->setStatusEffect(status, flag); + BLOCK_END("BeingNet::processBeingStatusChange") +} + +void BeingNet::processBeingMove2(Net::MessageIn &msg) +{ + BLOCK_START("BeingNet::processBeingMove2") + if (!actorManager) + { + BLOCK_END("BeingNet::processBeingMove2") + return; + } + + /* + * A simplified movement packet, used by the + * later versions of eAthena for both mobs and + * players + */ + Being *const dstBeing = actorManager->findBeing( + msg.readBeingId("being id")); + + uint16_t srcX, srcY, dstX, dstY; + msg.readCoordinatePair(srcX, srcY, dstX, dstY, "move path"); + msg.readUInt8("(sx<<4) | (sy&0x0f)"); + msg.readInt32("tick"); + + /* + * This packet doesn't have enough info to actually + * create a new being, so if the being isn't found, + * we'll just pretend the packet didn't happen + */ + + if (!dstBeing) + { + BLOCK_END("BeingNet::processBeingMove2") + return; + } + + dstBeing->setTileCoords(srcX, srcY); + if (localPlayer) + localPlayer->followMoveTo(dstBeing, srcX, srcY, dstX, dstY); + if (!serverFeatures->haveMove3()) + dstBeing->setDestination(dstX, dstY); + if (dstBeing->getType() == ActorType::Player) + dstBeing->setMoveTime(); + BLOCK_END("BeingNet::processBeingMove2") +} + +void BeingNet::processBeingAction2(Net::MessageIn &msg) +{ + BLOCK_START("BeingNet::processBeingAction2") + if (!actorManager) + { + BLOCK_END("BeingNet::processBeingAction2") + return; + } + + Being *const srcBeing = actorManager->findBeing( + msg.readBeingId("src being id")); + Being *const dstBeing = actorManager->findBeing( + msg.readBeingId("dst being id")); + + msg.readInt32("tick"); + const int srcSpeed = msg.readInt32("src speed"); + msg.readInt32("dst speed"); + const int param1 = msg.readInt32("damage"); + msg.readInt16("count"); + const AttackTypeT type = static_cast<AttackTypeT>( + msg.readUInt8("action")); + msg.readInt32("left damage"); + + switch (type) + { + case AttackType::HIT: // Damage + case AttackType::CRITICAL: // Critical Damage + case AttackType::MULTI: // Critical Damage + case AttackType::MULTI_REFLECT: + case AttackType::REFLECT: // Reflected Damage + case AttackType::FLEE: // Lucky Dodge + case AttackType::SPLASH: + case AttackType::SKILL: + case AttackType::REPEATE: + if (srcBeing) + { + if (srcSpeed && srcBeing->getType() == ActorType::Player) + srcBeing->setAttackDelay(srcSpeed); + // attackid=1, type + srcBeing->handleAttack(dstBeing, param1, 1); + if (srcBeing->getType() == ActorType::Player) + srcBeing->setAttackTime(); + } + if (dstBeing) + { + // level not present, using 1 + dstBeing->takeDamage(srcBeing, param1, + static_cast<AttackTypeT>(type), 1); + } + break; + + case AttackType::PICKUP: + break; + + case AttackType::TOUCH_SKILL: + break; + + case AttackType::SIT: + if (srcBeing) + { + srcBeing->setAction(BeingAction::SIT, 0); + if (srcBeing->getType() == ActorType::Player) + { + srcBeing->setMoveTime(); + if (localPlayer) + localPlayer->imitateAction(srcBeing, BeingAction::SIT); + } + } + break; + + case AttackType::STAND: + if (srcBeing) + { + srcBeing->setAction(BeingAction::STAND, 0); + if (srcBeing->getType() == ActorType::Player) + { + srcBeing->setMoveTime(); + if (localPlayer) + { + localPlayer->imitateAction(srcBeing, + BeingAction::STAND); + } + } + } + break; + default: + case AttackType::MISS: + case AttackType::SKILLMISS: + UNIMPLIMENTEDPACKET; + break; + } + BLOCK_END("BeingNet::processBeingAction2") +} + +void BeingNet::processMonsterHp(Net::MessageIn &msg) +{ + Being *const dstBeing = actorManager->findBeing( + msg.readBeingId("monster id")); + const int hp = msg.readInt32("hp"); + const int maxHP = msg.readInt32("max hp"); + if (dstBeing) + { + dstBeing->setHP(hp); + dstBeing->setMaxHP(maxHP); + } +} + +void BeingNet::processSkillAutoCast(Net::MessageIn &msg) +{ + const int id = msg.readInt16("skill id"); + msg.readInt16("inf"); + msg.readInt16("unused"); + const int level = msg.readInt16("skill level"); + msg.readInt16("sp"); + msg.readInt16("range"); + msg.readString(24, "skill name"); + msg.readInt8("unused"); + + if (localPlayer) + { + localPlayer->handleSkill(localPlayer, 0, id, level); + localPlayer->takeDamage(localPlayer, 0, AttackType::SKILL, id, level); + } +} + +void BeingNet::processRanksList(Net::MessageIn &msg) +{ + UNIMPLIMENTEDPACKET; + // +++ here need window with rank tables. + msg.readInt16("rank type"); + for (int f = 0; f < 10; f ++) + msg.readString(24, "name"); + for (int f = 0; f < 10; f ++) + msg.readInt32("points"); + msg.readInt32("my points"); +} + +void BeingNet::processBlacksmithRanksList(Net::MessageIn &msg) +{ + UNIMPLIMENTEDPACKET; + // +++ here need window with rank tables. + for (int f = 0; f < 10; f ++) + msg.readString(24, "name"); + for (int f = 0; f < 10; f ++) + msg.readInt32("points"); +} + +void BeingNet::processAlchemistRanksList(Net::MessageIn &msg) +{ + UNIMPLIMENTEDPACKET; + // +++ here need window with rank tables. + for (int f = 0; f < 10; f ++) + msg.readString(24, "name"); + for (int f = 0; f < 10; f ++) + msg.readInt32("points"); +} + +void BeingNet::processTaekwonRanksList(Net::MessageIn &msg) +{ + UNIMPLIMENTEDPACKET; + // +++ here need window with rank tables. + for (int f = 0; f < 10; f ++) + msg.readString(24, "name"); + for (int f = 0; f < 10; f ++) + msg.readInt32("points"); +} + +void BeingNet::processPkRanksList(Net::MessageIn &msg) +{ + UNIMPLIMENTEDPACKET; + // +++ here need window with rank tables. + for (int f = 0; f < 10; f ++) + msg.readString(24, "name"); + for (int f = 0; f < 10; f ++) + msg.readInt32("points"); +} + +void BeingNet::processBeingChangeDirection(Net::MessageIn &msg) +{ + BLOCK_START("BeingNet::processBeingChangeDirection") + if (!actorManager) + { + BLOCK_END("BeingNet::processBeingChangeDirection") + return; + } + + Being *const dstBeing = actorManager->findBeing( + msg.readBeingId("being id")); + + msg.readInt16("head direction"); + + const uint8_t dir = Net::MessageIn::fromServerDirection( + static_cast<uint8_t>(msg.readUInt8("player direction") & 0x0FU)); + + if (!dstBeing) + { + BLOCK_END("BeingNet::processBeingChangeDirection") + return; + } + + dstBeing->setDirection(dir); + if (localPlayer) + localPlayer->imitateDirection(dstBeing, dir); + BLOCK_END("BeingNet::processBeingChangeDirection") +} + +void BeingNet::processBeingSpecialEffect(Net::MessageIn &msg) +{ + if (!effectManager || !actorManager) + return; + + const BeingId id = msg.readBeingId("being id"); + Being *const being = actorManager->findBeing(id); + if (!being) + return; + + const int effectType = msg.readInt32("effect type"); + + if (Particle::enabled) + effectManager->trigger(effectType, being); + + // +++ need dehard code effectType == 3 + if (effectType == 3 && being->getType() == ActorType::Player + && socialWindow) + { // reset received damage + socialWindow->resetDamage(being->getName()); + } +} + +void BeingNet::processBeingSpecialEffectNum(Net::MessageIn &msg) +{ + UNIMPLIMENTEDPACKET; + // +++ need somhow show this effects. + // type is not same with self/misc effect. + msg.readBeingId("account id"); + msg.readInt32("effect type"); + msg.readInt32("num"); // effect variable +} + +void BeingNet::processBeingSoundEffect(Net::MessageIn &msg) +{ + UNIMPLIMENTEDPACKET; + // +++ need play this effect. + msg.readString(24, "sound effect name"); + msg.readUInt8("type"); + msg.readInt32("unused"); + msg.readInt32("source being id"); +} + +void BeingNet::processSkillGroundNoDamage(Net::MessageIn &msg) +{ + UNIMPLIMENTEDPACKET; + msg.readInt16("skill id"); + msg.readInt32("src id"); + msg.readInt16("val"); + msg.readInt16("x"); + msg.readInt16("y"); + msg.readInt32("tick"); +} + +void BeingNet::processSkillEntry(Net::MessageIn &msg) +{ + UNIMPLIMENTEDPACKET; + msg.readInt16("len"); + msg.readInt32("accound id"); + msg.readInt32("creator accound id"); + msg.readInt16("x"); + msg.readInt16("y"); + msg.readInt32("job"); + msg.readUInt8("radius"); + msg.readUInt8("visible"); + msg.readUInt8("level"); +} + +void BeingNet::processPlaterStatusChange(Net::MessageIn &msg) +{ + BLOCK_START("BeingNet::processPlayerStop") + if (!actorManager) + { + BLOCK_END("BeingNet::processPlayerStop") + return; + } + + // Change in players' flags + const BeingId id = msg.readBeingId("account id"); + Being *const dstBeing = actorManager->findBeing(id); + if (!dstBeing) + return; + + const uint16_t stunMode = msg.readInt16("stun mode"); + uint32_t statusEffects = msg.readInt16("status effect"); + statusEffects |= (static_cast<uint32_t>(msg.readInt32("opt?"))) << 16; + dstBeing->setKarma(msg.readUInt8("karma")); + + dstBeing->setStunMode(stunMode); + dstBeing->setStatusEffectBlock(0, static_cast<uint16_t>( + (statusEffects >> 16) & 0xffff)); + dstBeing->setStatusEffectBlock(16, static_cast<uint16_t>( + statusEffects & 0xffff)); + BLOCK_END("BeingNet::processPlayerStop") +} + +void BeingNet::processPlaterStatusChange2(Net::MessageIn &msg) +{ + if (!actorManager) + return; + + const BeingId id = msg.readBeingId("account id"); + Being *const dstBeing = actorManager->findBeing(id); + if (!dstBeing) + return; + + uint32_t statusEffects = msg.readInt32("status effect"); + dstBeing->setLevel(msg.readInt32("level")); + msg.readInt32("showEFST"); + + dstBeing->setStatusEffectBlock(0, static_cast<uint16_t>( + (statusEffects >> 16) & 0xffff)); + dstBeing->setStatusEffectBlock(16, static_cast<uint16_t>( + statusEffects & 0xffff)); +} + +void BeingNet::processPlaterStatusChangeNoTick(Net::MessageIn &msg) +{ + const uint16_t status = msg.readInt16("index"); + const BeingId id = msg.readBeingId("account id"); + const Enable flag = fromBool(msg.readUInt8("state") + ? true : false, Enable); + + Being *const dstBeing = actorManager->findBeing(id); + if (!dstBeing) + return; + + dstBeing->setStatusEffect(status, flag); +} + +void BeingNet::processBeingResurrect(Net::MessageIn &msg) +{ + BLOCK_START("BeingNet::processBeingResurrect") + if (!actorManager || !localPlayer) + { + BLOCK_END("BeingNet::processBeingResurrect") + return; + } + + // A being changed mortality status + + const BeingId id = msg.readBeingId("being id"); + msg.readInt16("unused"); + Being *const dstBeing = actorManager->findBeing(id); + if (!dstBeing) + { + BLOCK_END("BeingNet::processBeingResurrect") + return; + } + + // If this is player's current target, clear it. + if (dstBeing == localPlayer->getTarget()) + localPlayer->stopAttack(); + + dstBeing->setAction(BeingAction::STAND, 0); + BLOCK_END("BeingNet::processBeingResurrect") +} + +void BeingNet::processPlayerGuilPartyInfo(Net::MessageIn &msg) +{ + BLOCK_START("BeingNet::processPlayerGuilPartyInfo") + if (!actorManager) + { + BLOCK_END("BeingNet::processPlayerGuilPartyInfo") + return; + } + + Being *const dstBeing = actorManager->findBeing( + msg.readBeingId("being id")); + + if (dstBeing) + { + dstBeing->setName(msg.readString(24, "char name")); + dstBeing->setPartyName(msg.readString(24, "party name")); + dstBeing->setGuildName(msg.readString(24, "guild name")); + dstBeing->setGuildPos(msg.readString(24, "guild pos")); + dstBeing->addToCache(); + } + BLOCK_END("BeingNet::processPlayerGuilPartyInfo") +} + +void BeingNet::processBeingRemoveSkil(Net::MessageIn &msg) +{ + UNIMPLIMENTEDPACKET; + // +++ if skill unit was added, here need remove it from actors + msg.readInt32("skill unit id"); +} + +void BeingNet::processBeingFakeName(Net::MessageIn &msg) +{ + const BeingType::BeingType type = static_cast<BeingType::BeingType>( + msg.readUInt8("object type")); + const BeingId id = msg.readBeingId("npc id"); + msg.skip(8, "unused"); + const uint16_t job = msg.readInt16("class?"); // 111 + msg.skip(30, "unused"); + uint16_t x, y; + uint8_t dir; + msg.readCoordinates(x, y, dir, "position"); + msg.readUInt8("sx"); + msg.readUInt8("sy"); + msg.skip(4, "unsued"); + + Being *const dstBeing = createBeing2(msg, id, job, type); + if (!dstBeing) + return; + dstBeing->setSubtype(fromInt(job, BeingTypeId), 0); + dstBeing->setTileCoords(x, y); + dstBeing->setDirection(dir); +} + +void BeingNet::processBeingStatUpdate1(Net::MessageIn &msg) +{ + const BeingId id = msg.readBeingId("account id"); + const int type = msg.readInt16("type"); + const int value = msg.readInt32("value"); + + Being *const dstBeing = actorManager->findBeing(id); + if (!dstBeing) + return; + + if (type != Ea::MANNER) + { + UNIMPLIMENTEDPACKET; + return; + } + dstBeing->setManner(value); +} + +void BeingNet::processBeingSelfEffect(Net::MessageIn &msg) +{ + BLOCK_START("BeingNet::processBeingSelfEffect") + if (!effectManager || !actorManager) + { + BLOCK_END("BeingNet::processBeingSelfEffect") + return; + } + + const BeingId id = msg.readBeingId("being id"); + Being *const being = actorManager->findBeing(id); + if (!being) + { + BLOCK_END("BeingNet::processBeingSelfEffect") + return; + } + + const int effectType = msg.readInt32("effect type"); + if (Particle::enabled) + effectManager->trigger(effectType, being); + + BLOCK_END("BeingNet::processBeingSelfEffect") +} + +void BeingNet::processMobInfo(Net::MessageIn &msg) +{ + const int len = msg.readInt16("len"); + if (len < 12) + return; + Being *const dstBeing = actorManager->findBeing( + msg.readBeingId("monster id")); + const int attackRange = msg.readInt32("range"); + if (dstBeing) + dstBeing->setAttackRange(attackRange); +} + +void BeingNet::processBeingAttrs(Net::MessageIn &msg) +{ + const int len = msg.readInt16("len"); + if (len < 12) + return; + Being *const dstBeing = actorManager->findBeing( + msg.readBeingId("player id")); + const int gmLevel = msg.readInt32("gm level"); + if (dstBeing && gmLevel) + { + if (dstBeing == localPlayer) + localPlayer->setGMLevel(gmLevel); + dstBeing->setGM(true); + } + else + { + if (dstBeing == localPlayer) + localPlayer->setGMLevel(0); + if (dstBeing) + dstBeing->setGM(false); + } +} + +void BeingNet::processMonsterInfo(Net::MessageIn &msg) +{ + UNIMPLIMENTEDPACKET; + + msg.readInt16("class"); + msg.readInt16("level"); + msg.readInt16("size"); + msg.readInt32("hp"); + msg.readInt16("def"); + msg.readInt16("race"); + msg.readInt16("mdef"); + msg.readInt16("ele"); +} + +void BeingNet::processClassChange(Net::MessageIn &msg) +{ + UNIMPLIMENTEDPACKET; + + msg.readBeingId("being id"); + msg.readUInt8("type"); + msg.readInt32("class"); +} + +void BeingNet::processSpiritBalls(Net::MessageIn &msg) +{ + UNIMPLIMENTEDPACKET; + + msg.readBeingId("being id"); + msg.readInt16("spirits amount"); +} + +void BeingNet::processSpiritBallSingle(Net::MessageIn &msg) +{ + UNIMPLIMENTEDPACKET; + + msg.readBeingId("being id"); + msg.readInt16("spirits amount"); +} + +void BeingNet::processBladeStop(Net::MessageIn &msg) +{ + UNIMPLIMENTEDPACKET; + + msg.readInt32("src being id"); + msg.readInt32("dst being id"); + msg.readInt32("flag"); +} + +void BeingNet::processComboDelay(Net::MessageIn &msg) +{ + UNIMPLIMENTEDPACKET; + + msg.readBeingId("being id"); + msg.readInt32("wait"); +} + +void BeingNet::processWddingEffect(Net::MessageIn &msg) +{ + UNIMPLIMENTEDPACKET; + + msg.readBeingId("being id"); +} + +void BeingNet::processBeingSlide(Net::MessageIn &msg) +{ + UNIMPLIMENTEDPACKET; + + msg.readBeingId("being id"); + msg.readInt16("x"); + msg.readInt16("y"); +} + +void BeingNet::processStarsKill(Net::MessageIn &msg) +{ + UNIMPLIMENTEDPACKET; + + msg.readString(24, "map name"); + msg.readInt32("monster id"); + msg.readUInt8("start"); + msg.readUInt8("result"); +} + +void BeingNet::processGladiatorFeelRequest(Net::MessageIn &msg) +{ + UNIMPLIMENTEDPACKET; + + msg.readUInt8("which"); +} + +void BeingNet::processBossMapInfo(Net::MessageIn &msg) +{ + UNIMPLIMENTEDPACKET; + + msg.readUInt8("info type"); + msg.readInt32("x"); + msg.readInt32("y"); + msg.readInt16("min hours"); + msg.readInt16("min minutes"); + msg.readInt16("max hours"); + msg.readInt16("max minutes"); + msg.readString(24, "monster name"); // really can be used 51 byte? +} + +void BeingNet::processBeingFont(Net::MessageIn &msg) +{ + UNIMPLIMENTEDPACKET; + + msg.readBeingId("account id"); + msg.readInt16("font"); +} + +void BeingNet::processBeingMilleniumShield(Net::MessageIn &msg) +{ + UNIMPLIMENTEDPACKET; + + msg.readBeingId("account id"); + msg.readInt16("shields"); + msg.readInt16("unused"); +} + +void BeingNet::processBeingCharm(Net::MessageIn &msg) +{ + UNIMPLIMENTEDPACKET; + + msg.readBeingId("account id"); + msg.readInt16("charm type"); + msg.readInt16("charm count"); +} + +void BeingNet::processBeingViewEquipment(Net::MessageIn &msg) +{ + UNIMPLIMENTEDPACKET; + + const int count = (msg.readInt16("len") - 45) / 31; + msg.readString(24, "name"); + msg.readInt16("job"); + msg.readInt16("head"); + msg.readInt16("accessory"); + msg.readInt16("accessory2"); + msg.readInt16("accessory3"); + msg.readInt16("robe"); + msg.readInt16("hair color"); + msg.readInt16("body color"); + msg.readUInt8("gender"); + for (int f = 0; f < count; f ++) + { + msg.readInt16("index"); + msg.readInt16("item id"); + msg.readUInt8("item type"); + msg.readInt32("location"); + msg.readInt32("wear state"); + msg.readInt8("refine"); + for (int d = 0; d < 4; d ++) + msg.readInt16("card"); + msg.readInt32("hire expire date (?)"); + msg.readInt16("equip type"); + msg.readInt16("item sprite number"); + msg.readUInt8("flags"); + } +} + +void BeingNet::processPvpSet(Net::MessageIn &msg) +{ + BLOCK_START("BeingNet::processPvpSet") + const BeingId id = msg.readBeingId("being id"); + const int rank = msg.readInt32("rank"); + msg.readInt32("num"); + if (actorManager) + { + Being *const dstBeing = actorManager->findBeing(id); + if (dstBeing) + dstBeing->setPvpRank(rank); + } + BLOCK_END("BeingNet::processPvpSet") +} + +void BeingNet::processNameResponse2(Net::MessageIn &msg) +{ + BLOCK_START("BeingNet::processNameResponse2") + if (!actorManager || !localPlayer) + { + BLOCK_END("BeingNet::processNameResponse2") + return; + } + + const int len = msg.readInt16("len"); + const BeingId beingId = msg.readBeingId("account ic"); + const std::string str = msg.readString(len - 8, "name"); + Being *const dstBeing = actorManager->findBeing(beingId); + if (dstBeing) + { + if (beingId == localPlayer->getId()) + { + localPlayer->pingResponse(); + } + else + { + dstBeing->setName(str); + dstBeing->updateGuild(); + dstBeing->addToCache(); + + if (dstBeing->getType() == ActorType::Player) + dstBeing->updateColors(); + + if (localPlayer) + { + const Party *const party = localPlayer->getParty(); + if (party && party->isMember(dstBeing->getId())) + { + PartyMember *const member = party->getMember( + dstBeing->getId()); + + if (member) + member->setName(dstBeing->getName()); + } + localPlayer->checkNewName(dstBeing); + } + } + } + BLOCK_END("BeingNet::processNameResponse2") +} + +Being *BeingNet::createBeing2(Net::MessageIn &msg, + const BeingId id, + const int16_t job, + const BeingType::BeingType beingType) +{ + if (!actorManager) + return nullptr; + + ActorTypeT type = ActorType::Unknown; + switch (beingType) + { + case BeingType::PC: + type = ActorType::Player; + break; + case BeingType::NPC: + case BeingType::NPC_EVENT: + type = ActorType::Npc; + break; + case BeingType::MONSTER: + type = ActorType::Monster; + break; + case BeingType::MERSOL: + type = ActorType::Mercenary; + break; + case BeingType::PET: + type = ActorType::Pet; + break; + case BeingType::HOMUN: + type = ActorType::Homunculus; + break; + case BeingType::ITEM: + case BeingType::SKILL: + case BeingType::ELEMENTAL: + logger->log("not supported object type: %d, job: %d", + static_cast<int>(beingType), static_cast<int>(job)); + break; + case BeingType::CHAT: + default: + UNIMPLIMENTEDPACKET; + type = ActorType::Monster; + logger->log("not supported object type: %d, job: %d", + static_cast<int>(beingType), static_cast<int>(job)); + break; + } + if (job == 45 && beingType == BeingType::NPC_EVENT) + type = ActorType::Portal; + + Being *const being = actorManager->createBeing( + id, type, fromInt(job, BeingTypeId)); + if (beingType == BeingType::MERSOL) + { + MercenaryInfo *const info = PlayerInfo::getMercenary(); + if (info && info->id == id) + PlayerInfo::setMercenaryBeing(being); + } + else if (beingType == BeingType::PET) + { + if (PlayerInfo::getPetBeingId() == id) + PlayerInfo::setPetBeing(being); + } + return being; +} + +} // namespace EAthena diff --git a/src/net/eathena/beingnet.h b/src/net/eathena/beingnet.h new file mode 100644 index 000000000..0b3dd041f --- /dev/null +++ b/src/net/eathena/beingnet.h @@ -0,0 +1,101 @@ +/* + * The ManaPlus Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011-2015 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef NET_EATHENA_BEING_H +#define NET_EATHENA_BEING_H + +#include "net/ea/beinghandler.h" + +#include "net/eathena/beingtype.h" +#include "net/eathena/messagehandler.h" + +namespace EAthena +{ + namespace BeingNet + { + void processBeingChangeLook2(Net::MessageIn &msg); + void processBeingChangeLookCards(Net::MessageIn &msg); + void processBeingVisible(Net::MessageIn &msg); + void processBeingMove(Net::MessageIn &msg); + void processBeingSpawn(Net::MessageIn &msg); + void processMapTypeProperty(Net::MessageIn &msg); + void processMapType(Net::MessageIn &msg); + void processSkillCasting(Net::MessageIn &msg); + void processBeingStatusChange(Net::MessageIn &msg); + void processBeingStatusChange2(Net::MessageIn &msg); + void processBeingMove2(Net::MessageIn &msg); + void processBeingAction2(Net::MessageIn &msg); + void processMonsterHp(Net::MessageIn &msg); + void processSkillAutoCast(Net::MessageIn &msg); + void processRanksList(Net::MessageIn &msg); + void processBeingChangeDirection(Net::MessageIn &msg); + void processBeingChangeLookContinue(Net::MessageIn &msg, + Being *const dstBeing, + const uint8_t type, + const int id, + const int id2, + const int *cards) A_NONNULL(2); + void processBeingSpecialEffect(Net::MessageIn &msg); + void processBeingSpecialEffectNum(Net::MessageIn &msg); + void processBeingSoundEffect(Net::MessageIn &msg); + void processSkillGroundNoDamage(Net::MessageIn &msg); + void processSkillEntry(Net::MessageIn &msg); + void processPlaterStatusChange(Net::MessageIn &msg); + void processPlaterStatusChangeNoTick(Net::MessageIn &msg); + void processBeingResurrect(Net::MessageIn &msg); + void processPlayerGuilPartyInfo(Net::MessageIn &msg); + void processBeingRemoveSkil(Net::MessageIn &msg); + void processBeingFakeName(Net::MessageIn &msg); + void processBeingStatUpdate1(Net::MessageIn &msg); + void processPlaterStatusChange2(Net::MessageIn &msg); + void processBeingSelfEffect(Net::MessageIn &msg); + void processMobInfo(Net::MessageIn &msg); + void processBeingAttrs(Net::MessageIn &msg); + void processMonsterInfo(Net::MessageIn &msg); + void processClassChange(Net::MessageIn &msg); + void processSpiritBalls(Net::MessageIn &msg); + void processSpiritBallSingle(Net::MessageIn &msg); + void processBladeStop(Net::MessageIn &msg); + void processComboDelay(Net::MessageIn &msg); + void processWddingEffect(Net::MessageIn &msg); + void processBeingSlide(Net::MessageIn &msg); + void processStarsKill(Net::MessageIn &msg); + void processBlacksmithRanksList(Net::MessageIn &msg); + void processAlchemistRanksList(Net::MessageIn &msg); + void processTaekwonRanksList(Net::MessageIn &msg); + void processPkRanksList(Net::MessageIn &msg); + void processGladiatorFeelRequest(Net::MessageIn &msg); + void processBossMapInfo(Net::MessageIn &msg); + void processBeingFont(Net::MessageIn &msg); + void processBeingMilleniumShield(Net::MessageIn &msg); + void processBeingCharm(Net::MessageIn &msg); + void processBeingViewEquipment(Net::MessageIn &msg); + void processPvpSet(Net::MessageIn &msg); + void processNameResponse2(Net::MessageIn &msg); + Being *createBeing2(Net::MessageIn &msg, + const BeingId id, + const int16_t job, + const BeingType::BeingType beingType); + } // namespace Being +} // namespace EAthena + +#endif // NET_EATHENA_BEING_H diff --git a/src/net/tmwa/beinghandler.cpp b/src/net/tmwa/beinghandler.cpp index d34594ee6..2ec3e62cb 100644 --- a/src/net/tmwa/beinghandler.cpp +++ b/src/net/tmwa/beinghandler.cpp @@ -38,6 +38,9 @@ #include "net/serverfeatures.h" +#include "net/ea/beingnet.h" + +#include "net/tmwa/beingnet.h" #include "net/tmwa/guildmanager.h" #include "net/tmwa/messageout.h" #include "net/tmwa/protocol.h" @@ -112,122 +115,122 @@ void BeingHandler::handleMessage(Net::MessageIn &msg) switch (msg.getId()) { case SMSG_BEING_VISIBLE: - processBeingVisible(msg); + BeingNet::processBeingVisible(msg); break; case SMSG_BEING_MOVE: - processBeingMove(msg); + BeingNet::processBeingMove(msg); break; case SMSG_BEING_MOVE2: - processBeingMove2(msg); + BeingNet::processBeingMove2(msg); break; case SMSG_BEING_MOVE3: - processBeingMove3(msg); + Ea::BeingNet::processBeingMove3(msg); break; case SMSG_BEING_SPAWN: - processBeingSpawn(msg); + BeingNet::processBeingSpawn(msg); break; case SMSG_BEING_REMOVE: - processBeingRemove(msg); + Ea::BeingNet::processBeingRemove(msg); break; case SMSG_BEING_RESURRECT: - processBeingResurrect(msg); + BeingNet::processBeingResurrect(msg); break; case SMSG_SKILL_DAMAGE: - processSkillDamage(msg); + Ea::BeingNet::processSkillDamage(msg); break; case SMSG_BEING_ACTION: - processBeingAction(msg); + Ea::BeingNet::processBeingAction(msg); break; case SMSG_BEING_SELFEFFECT: - processBeingSelfEffect(msg); + BeingNet::processBeingSelfEffect(msg); break; case SMSG_BEING_EMOTION: - processBeingEmotion(msg); + Ea::BeingNet::processBeingEmotion(msg); break; case SMSG_BEING_CHANGE_LOOKS: - processBeingChangeLook(msg); + BeingNet::processBeingChangeLook(msg); break; case SMSG_BEING_CHANGE_LOOKS2: - processBeingChangeLook2(msg); + BeingNet::processBeingChangeLook2(msg); break; case SMSG_BEING_NAME_RESPONSE: - processNameResponse(msg); + Ea::BeingNet::processNameResponse(msg); break; case SMSG_BEING_IP_RESPONSE: - processIpResponse(msg); + BeingNet::processIpResponse(msg); break; case SMSG_SOLVE_CHAR_NAME: break; case SMSG_PLAYER_GUILD_PARTY_INFO: - processPlayerGuilPartyInfo(msg); + BeingNet::processPlayerGuilPartyInfo(msg); break; case SMSG_BEING_CHANGE_DIRECTION: - processBeingChangeDirection(msg); + BeingNet::processBeingChangeDirection(msg); break; case SMSG_PLAYER_UPDATE_1: - processPlayerUpdate1(msg); + BeingNet::processPlayerUpdate1(msg); break; case SMSG_PLAYER_UPDATE_2: - processPlayerUpdate2(msg); + BeingNet::processPlayerUpdate2(msg); break; case SMSG_PLAYER_MOVE: - processPlayerMove(msg); + BeingNet::processPlayerMove(msg); break; case SMSG_PLAYER_STOP: - processPlayerStop(msg); + Ea::BeingNet::processPlayerStop(msg); break; case SMSG_PLAYER_MOVE_TO_ATTACK: - processPlayerMoveToAttack(msg); + Ea::BeingNet::processPlayerMoveToAttack(msg); break; case SMSG_PLAYER_STATUS_CHANGE: - processPlaterStatusChange(msg); + BeingNet::processPlaterStatusChange(msg); break; case SMSG_BEING_STATUS_CHANGE: - processBeingStatusChange(msg); + BeingNet::processBeingStatusChange(msg); break; case SMSG_SKILL_CASTING: - processSkillCasting(msg); + BeingNet::processSkillCasting(msg); break; case SMSG_SKILL_CAST_CANCEL: - processSkillCastCancel(msg); + BeingNet::processSkillCastCancel(msg); break; case SMSG_SKILL_NO_DAMAGE: - processSkillNoDamage(msg); + Ea::BeingNet::processSkillNoDamage(msg); break; case SMSG_PVP_MAP_MODE: - processPvpMapMode(msg); + Ea::BeingNet::processPvpMapMode(msg); break; case SMSG_PVP_SET: - processPvpSet(msg); + BeingNet::processPvpSet(msg); break; default: @@ -254,1335 +257,8 @@ void BeingHandler::requestRanks(const RankT rank A_UNUSED) const } #endif -void BeingHandler::processBeingChangeLook(Net::MessageIn &msg) -{ - BLOCK_START("BeingHandler::processBeingChangeLook") - if (!actorManager) - { - BLOCK_END("BeingHandler::processBeingChangeLook") - return; - } - - Being *const dstBeing = actorManager->findBeing( - msg.readBeingId("being id")); - - const uint8_t type = msg.readUInt8("type"); - const int16_t id = static_cast<int16_t>(msg.readUInt8("id")); - const int id2 = 1; - - if (!localPlayer || !dstBeing) - { - BLOCK_END("BeingHandler::processBeingChangeLook") - return; - } - processBeingChangeLookContinue(msg, dstBeing, type, id, id2); - BLOCK_END("BeingHandler::processBeingChangeLook") -} - -void BeingHandler::processBeingChangeLook2(Net::MessageIn &msg) -{ - BLOCK_START("BeingHandler::processBeingChangeLook") - if (!actorManager) - { - BLOCK_END("BeingHandler::processBeingChangeLook") - return; - } - - Being *const dstBeing = actorManager->findBeing( - msg.readBeingId("being id")); - - const uint8_t type = msg.readUInt8("type"); - int id2 = 0; - - const int16_t id = msg.readInt16("id1"); - if (type == 2) - { - id2 = msg.readInt16("id2"); - } - else - { - msg.readInt16("id2"); - id2 = 1; - } - - if (!localPlayer || !dstBeing) - { - BLOCK_END("BeingHandler::processBeingChangeLook") - return; - } - processBeingChangeLookContinue(msg, dstBeing, type, id, id2); - BLOCK_END("BeingHandler::processBeingChangeLook") -} - -void BeingHandler::processBeingChangeLookContinue(Net::MessageIn &msg, - Being *const dstBeing, - const uint8_t type, - const int id, - const int id2) -{ - if (dstBeing->getType() == ActorType::Player) - dstBeing->setOtherTime(); - - const std::string color; - switch (type) - { - case 0: // change race - dstBeing->setSubtype(fromInt(id, BeingTypeId), - dstBeing->getLook()); - break; - case 1: // eAthena LOOK_HAIR - { - const uint16_t look = static_cast<uint16_t>(id / 256); - const int hair = id % 256; - dstBeing->setHairStyle(SPRITE_HAIR_COLOR, hair * -1); - dstBeing->setLook(look); - break; - } - case 2: // Weapon ID in id, Shield ID in id2 - dstBeing->setSprite(SPRITE_BODY, id, "", ItemColor_one, true); - dstBeing->setSprite(SPRITE_FLOOR, id2); - localPlayer->imitateOutfit(dstBeing, SPRITE_FLOOR); - break; - case 3: // Change lower headgear for eAthena, pants for us - dstBeing->setSprite(SPRITE_WEAPON, id, color, - ItemColor_one); - localPlayer->imitateOutfit(dstBeing, SPRITE_WEAPON); - break; - case 4: // Change upper headgear for eAthena, hat for us - dstBeing->setSprite(SPRITE_CLOTHES_COLOR, id, color, - ItemColor_one); - localPlayer->imitateOutfit(dstBeing, SPRITE_CLOTHES_COLOR); - break; - case 5: // Change middle headgear for eathena, armor for us - dstBeing->setSprite(SPRITE_HEAD_BOTTOM, id, color, - ItemColor_one); - localPlayer->imitateOutfit(dstBeing, SPRITE_HEAD_BOTTOM); - break; - case 6: // eAthena LOOK_HAIR_COLOR - dstBeing->setHairColor(SPRITE_HAIR_COLOR, - fromInt(id, ItemColor)); - break; - case 7: // Clothes color - // ignoring it - break; - case 8: // eAthena LOOK_SHIELD - dstBeing->setSprite(SPRITE_FLOOR, id, color, - ItemColor_one); - localPlayer->imitateOutfit(dstBeing, SPRITE_FLOOR); - break; - case 9: // eAthena LOOK_SHOES - dstBeing->setSprite(SPRITE_HAIR, id, color, - ItemColor_one); - localPlayer->imitateOutfit(dstBeing, SPRITE_HAIR); - break; - case 10: // LOOK_GLOVES - dstBeing->setSprite(SPRITE_SHOES, id, color, - ItemColor_one); - localPlayer->imitateOutfit(dstBeing, SPRITE_SHOES); - break; - case 11: // LOOK_CAPE - dstBeing->setSprite(SPRITE_SHIELD, id, color, - ItemColor_one); - localPlayer->imitateOutfit(dstBeing, SPRITE_SHIELD); - break; - case 12: - dstBeing->setSprite(SPRITE_HEAD_TOP, id, color, - ItemColor_one); - localPlayer->imitateOutfit(dstBeing, SPRITE_HEAD_TOP); - break; - case 13: - dstBeing->setSprite(SPRITE_HEAD_MID, id, color, - ItemColor_one); - localPlayer->imitateOutfit(dstBeing, SPRITE_HEAD_MID); - break; - case 14: - dstBeing->setSprite(SPRITE_ROBE, id, color, - ItemColor_one); - localPlayer->imitateOutfit(dstBeing, SPRITE_ROBE); - break; - case 15: - dstBeing->setSprite(SPRITE_EVOL2, id, color, - ItemColor_one); - localPlayer->imitateOutfit(dstBeing, SPRITE_EVOL2); - break; - case 16: - dstBeing->setLook(static_cast<uint16_t>(id)); - break; - default: - UNIMPLIMENTEDPACKET; - break; - } -} - -void BeingHandler::processPlayerUpdate1(Net::MessageIn &msg) -{ - BLOCK_START("BeingHandler::processPlayerMoveUpdate") - if (!actorManager || !localPlayer) - { - BLOCK_END("BeingHandler::processPlayerMoveUpdate") - return; - } - - // An update about a player, potentially including movement. - const BeingId id = msg.readBeingId("account id"); - const int16_t speed = msg.readInt16("speed"); - const uint16_t stunMode = msg.readInt16("opt1"); - uint32_t statusEffects = msg.readInt16("opt2"); - statusEffects |= (static_cast<uint32_t>(msg.readInt16("options"))) - << 16; - const int16_t job = msg.readInt16("job"); - int disguiseId = 0; - if (toInt(id, int) < 110000000 && job >= 1000) - disguiseId = job; - - Being *dstBeing = actorManager->findBeing(id); - if (!dstBeing) - { - if (actorManager->isBlocked(id) == true) - { - BLOCK_END("BeingHandler::processPlayerMoveUpdate") - return; - } - - dstBeing = createBeing(id, job); - - if (!dstBeing) - { - BLOCK_END("BeingHandler::processPlayerMoveUpdate") - return; - } - } - else if (disguiseId) - { - actorManager->undelete(dstBeing); - if (serverVersion < 1) - beingHandler->requestNameById(id); - } - - uint8_t dir = dstBeing->getDirectionDelayed(); - if (dir) - { - if (dir != dstBeing->getDirection()) - dstBeing->setDirection(dir); - } - - if (Party *const party = localPlayer->getParty()) - { - if (party->isMember(id)) - dstBeing->setParty(party); - } - - dstBeing->setWalkSpeed(Vector(speed, speed, 0)); - - const uint8_t hairStyle = msg.readUInt8("hair style"); - const uint16_t look = msg.readUInt8("look"); - dstBeing->setSubtype(fromInt(job, BeingTypeId), look); - const uint16_t weapon = msg.readInt16("weapon"); - const uint16_t shield = msg.readInt16("shield"); - const uint16_t headBottom = msg.readInt16("head bottom"); - - const uint16_t headTop = msg.readInt16("head top"); - const uint16_t headMid = msg.readInt16("head mid"); - const ItemColor hairColor = fromInt( - msg.readUInt8("hair color"), ItemColor); - msg.readUInt8("unused"); - msg.readInt32("unused"); - - const int guild = msg.readInt32("guild"); - - if (!guildManager || !GuildManager::getEnableGuildBot()) - { - if (guild == 0) - dstBeing->clearGuilds(); - else - dstBeing->setGuild(Guild::getGuild(static_cast<int16_t>(guild))); - } - - msg.readInt16("emblem"); - dstBeing->setManner(msg.readInt16("manner")); - dstBeing->setStatusEffectBlock(32, msg.readInt16("opt3")); - dstBeing->setKarma(msg.readUInt8("karma")); - // reserving bit for future usage - dstBeing->setGender(Being::intToGender( - static_cast<uint8_t>(msg.readUInt8("gender") & 3))); - - if (!disguiseId) - { - // Set these after the gender, as the sprites may be gender-specific - dstBeing->updateSprite(SPRITE_BODY, weapon, "", ItemColor_one, true); - dstBeing->updateSprite(SPRITE_FLOOR, shield); - dstBeing->updateSprite(SPRITE_WEAPON, headBottom); - dstBeing->updateSprite(SPRITE_HEAD_BOTTOM, headMid); - dstBeing->updateSprite(SPRITE_CLOTHES_COLOR, headTop); - dstBeing->updateSprite(SPRITE_HAIR_COLOR, hairStyle * -1, - ItemDB::get(-hairStyle).getDyeColorsString(hairColor)); - dstBeing->setHairColor(hairColor); - } - localPlayer->imitateOutfit(dstBeing); - - uint16_t x, y; - msg.readCoordinates(x, y, dir, "position"); - dstBeing->setTileCoords(x, y); - dstBeing->setDirection(dir); - - localPlayer->imitateDirection(dstBeing, dir); - - const uint16_t gmstatus = msg.readInt16("gm status"); - - if (gmstatus & 0x80) - dstBeing->setGM(true); - - applyPlayerAction(msg, dstBeing, msg.readUInt8("action type")); - const int level = static_cast<int>(msg.readUInt8("level")); - if (level) - dstBeing->setLevel(level); - - msg.readUInt8("unused"); - - dstBeing->setActionTime(tick_time); - - dstBeing->setStunMode(stunMode); - dstBeing->setStatusEffectBlock(0, static_cast<uint16_t>( - (statusEffects >> 16) & 0xffff)); - dstBeing->setStatusEffectBlock(16, static_cast<uint16_t>( - statusEffects & 0xffff)); - - BLOCK_END("BeingHandler::processPlayerMoveUpdate") -} - -void BeingHandler::processPlayerUpdate2(Net::MessageIn &msg) -{ - BLOCK_START("BeingHandler::processPlayerMoveUpdate") - if (!actorManager || !localPlayer) - { - BLOCK_END("BeingHandler::processPlayerMoveUpdate") - return; - } - - // An update about a player, potentially including movement. - const BeingId id = msg.readBeingId("account id"); - const int16_t speed = msg.readInt16("speed"); - const uint16_t stunMode = msg.readInt16("opt1"); - uint32_t statusEffects = msg.readInt16("opt2"); - statusEffects |= (static_cast<uint32_t>(msg.readInt16("options"))) - << 16; - const int16_t job = msg.readInt16("job"); - int disguiseId = 0; - if (toInt(id, int) < 110000000 && job >= 1000) - disguiseId = job; - - Being *dstBeing = actorManager->findBeing(id); - if (!dstBeing) - { - if (actorManager->isBlocked(id) == true) - { - BLOCK_END("BeingHandler::processPlayerMoveUpdate") - return; - } - - dstBeing = createBeing(id, job); - - if (!dstBeing) - { - BLOCK_END("BeingHandler::processPlayerMoveUpdate") - return; - } - } - else if (disguiseId) - { - actorManager->undelete(dstBeing); - if (serverVersion < 1) - beingHandler->requestNameById(id); - } - - uint8_t dir = dstBeing->getDirectionDelayed(); - if (dir) - { - if (dir != dstBeing->getDirection()) - dstBeing->setDirection(dir); - } - - if (Party *const party = localPlayer->getParty()) - { - if (party->isMember(id)) - dstBeing->setParty(party); - } - - dstBeing->setWalkSpeed(Vector(speed, speed, 0)); - - const uint8_t hairStyle = msg.readUInt8("hair style"); - const uint16_t look = msg.readUInt8("look"); - dstBeing->setSubtype(fromInt(job, BeingTypeId), look); - const uint16_t weapon = msg.readInt16("weapon"); - const uint16_t shield = msg.readInt16("shield"); - const uint16_t headBottom = msg.readInt16("head bottom"); - const uint16_t headTop = msg.readInt16("head top"); - const uint16_t headMid = msg.readInt16("head mid"); - const ItemColor hairColor = fromInt( - msg.readUInt8("hair color"), ItemColor); - msg.readUInt8("unused"); - msg.readInt32("unused"); - - const int guild = msg.readInt32("guild"); - - if (!guildManager || !GuildManager::getEnableGuildBot()) - { - if (guild == 0) - dstBeing->clearGuilds(); - else - dstBeing->setGuild(Guild::getGuild(static_cast<int16_t>(guild))); - } - - msg.readInt16("emblem"); - dstBeing->setManner(msg.readInt16("manner")); - dstBeing->setStatusEffectBlock(32, msg.readInt16("opt3")); - dstBeing->setKarma(msg.readUInt8("karma")); - // reserving bit for future usage - dstBeing->setGender(Being::intToGender( - static_cast<uint8_t>(msg.readUInt8("gender") & 3))); - - if (!disguiseId) - { - // Set these after the gender, as the sprites may be gender-specific - dstBeing->updateSprite(SPRITE_BODY, weapon, "", ItemColor_one, true); - dstBeing->updateSprite(SPRITE_FLOOR, shield); - dstBeing->updateSprite(SPRITE_WEAPON, headBottom); - dstBeing->updateSprite(SPRITE_HEAD_BOTTOM, headMid); - dstBeing->updateSprite(SPRITE_CLOTHES_COLOR, headTop); - dstBeing->updateSprite(SPRITE_HAIR_COLOR, hairStyle * -1, - ItemDB::get(-hairStyle).getDyeColorsString(hairColor)); - dstBeing->setHairColor(hairColor); - } - localPlayer->imitateOutfit(dstBeing); - - uint16_t x, y; - msg.readCoordinates(x, y, dir, "position"); - dstBeing->setTileCoords(x, y); - dstBeing->setDirection(dir); - - localPlayer->imitateDirection(dstBeing, dir); - - const uint16_t gmstatus = msg.readInt16("gm status"); - - if (gmstatus & 0x80) - dstBeing->setGM(true); - - applyPlayerAction(msg, dstBeing, msg.readUInt8("action type")); - const int level = static_cast<int>(msg.readUInt8("level")); - if (level) - dstBeing->setLevel(level); - - dstBeing->setActionTime(tick_time); - dstBeing->setStunMode(stunMode); - dstBeing->setStatusEffectBlock(0, static_cast<uint16_t>( - (statusEffects >> 16) & 0xffff)); - dstBeing->setStatusEffectBlock(16, static_cast<uint16_t>( - statusEffects & 0xffff)); - - BLOCK_END("BeingHandler::processPlayerMoveUpdate") -} - -void BeingHandler::processPlayerMove(Net::MessageIn &msg) -{ - BLOCK_START("BeingHandler::processPlayerMoveUpdate") - if (!actorManager || !localPlayer) - { - BLOCK_END("BeingHandler::processPlayerMoveUpdate") - return; - } - - // An update about a player, potentially including movement. - const BeingId id = msg.readBeingId("account id"); - const int16_t speed = msg.readInt16("speed"); - const uint16_t stunMode = msg.readInt16("opt1"); - uint32_t statusEffects = msg.readInt16("opt2"); - statusEffects |= (static_cast<uint32_t>(msg.readInt16("options"))) - << 16; - const int16_t job = msg.readInt16("job"); - int disguiseId = 0; - if (toInt(id, int) < 110000000 && job >= 1000) - disguiseId = job; - - Being *dstBeing = actorManager->findBeing(id); - if (!dstBeing) - { - if (actorManager->isBlocked(id) == true) - { - BLOCK_END("BeingHandler::processPlayerMoveUpdate") - return; - } - - dstBeing = createBeing(id, job); - - if (!dstBeing) - { - BLOCK_END("BeingHandler::processPlayerMoveUpdate") - return; - } - } - else if (disguiseId) - { - actorManager->undelete(dstBeing); - if (serverVersion < 1) - beingHandler->requestNameById(id); - } - - const uint8_t dir = dstBeing->getDirectionDelayed(); - if (dir) - { - if (dir != dstBeing->getDirection()) - dstBeing->setDirection(dir); - } - - if (Party *const party = localPlayer->getParty()) - { - if (party->isMember(id)) - dstBeing->setParty(party); - } - - dstBeing->setWalkSpeed(Vector(speed, speed, 0)); - - const uint8_t hairStyle = msg.readUInt8("hair style"); - const uint16_t look = msg.readUInt8("look"); - dstBeing->setSubtype(fromInt(job, BeingTypeId), look); - const uint16_t weapon = msg.readInt16("weapon"); - const uint16_t shield = msg.readInt16("shield"); - const uint16_t headBottom = msg.readInt16("head bottom"); - - msg.readInt32("tick"); - - const uint16_t headTop = msg.readInt16("head top"); - const uint16_t headMid = msg.readInt16("head mid"); - const ItemColor hairColor = fromInt( - msg.readUInt8("hair color"), ItemColor); - msg.readUInt8("unused"); - msg.readInt32("unused"); - - const int guild = msg.readInt32("guild"); - - if (!guildManager || !GuildManager::getEnableGuildBot()) - { - if (guild == 0) - dstBeing->clearGuilds(); - else - dstBeing->setGuild(Guild::getGuild(static_cast<int16_t>(guild))); - } - - msg.readInt16("emblem"); - dstBeing->setManner(msg.readInt16("manner")); - dstBeing->setStatusEffectBlock(32, msg.readInt16("opt3")); - dstBeing->setKarma(msg.readUInt8("karma")); - // reserving bit for future usage - dstBeing->setGender(Being::intToGender( - static_cast<uint8_t>(msg.readUInt8("gender") & 3))); - - if (!disguiseId) - { - // Set these after the gender, as the sprites may be gender-specific - dstBeing->updateSprite(SPRITE_BODY, weapon, "", ItemColor_one, true); - dstBeing->updateSprite(SPRITE_FLOOR, shield); - dstBeing->updateSprite(SPRITE_WEAPON, headBottom); - dstBeing->updateSprite(SPRITE_HEAD_BOTTOM, headMid); - dstBeing->updateSprite(SPRITE_CLOTHES_COLOR, headTop); - dstBeing->updateSprite(SPRITE_HAIR_COLOR, hairStyle * -1, - ItemDB::get(-hairStyle).getDyeColorsString(hairColor)); - dstBeing->setHairColor(hairColor); - } - localPlayer->imitateOutfit(dstBeing); - - uint16_t srcX, srcY, dstX, dstY; - msg.readCoordinatePair(srcX, srcY, dstX, dstY, "moving path"); - - localPlayer->followMoveTo(dstBeing, srcX, srcY, dstX, dstY); - - dstBeing->setTileCoords(srcX, srcY); - dstBeing->setDestination(dstX, dstY); - - // because server don't send direction in move packet, - // we fixing it - - if (srcX != dstX || srcY != dstY) - { - const int d = dstBeing->calcDirection(dstX, dstY); - - if (d && dstBeing->getDirection() != d) - dstBeing->setDirectionDelayed(static_cast<uint8_t>(d)); - } - - if (localPlayer->getCurrentAction() != BeingAction::STAND) - localPlayer->imitateAction(dstBeing, BeingAction::STAND); - if (localPlayer->getDirection() != dstBeing->getDirection()) - { - localPlayer->imitateDirection(dstBeing, - dstBeing->getDirection()); - } - - const uint16_t gmstatus = msg.readInt16("gm status"); - - if (gmstatus & 0x80) - dstBeing->setGM(true); - - msg.readUInt8("unused"); - - const int level = static_cast<int>(msg.readUInt8("level")); - if (level) - dstBeing->setLevel(level); - - msg.readUInt8("unused"); - - if (dstBeing->getType() != ActorType::Player) - dstBeing->setActionTime(tick_time); - - dstBeing->setStunMode(stunMode); - dstBeing->setStatusEffectBlock(0, static_cast<uint16_t>( - (statusEffects >> 16) & 0xffff)); - dstBeing->setStatusEffectBlock(16, static_cast<uint16_t>( - statusEffects & 0xffff)); - - if (dstBeing->getType() == ActorType::Player) - dstBeing->setMoveTime(); - BLOCK_END("BeingHandler::processPlayerMoveUpdate") -} - -void BeingHandler::processBeingVisible(Net::MessageIn &msg) -{ - BLOCK_START("BeingHandler::processBeingVisibleOrMove") - if (!actorManager) - { - BLOCK_END("BeingHandler::processBeingVisibleOrMove") - return; - } - - BeingId spawnId; - - // Information about a being in range - const BeingId id = msg.readBeingId("being id"); - if (id == mSpawnId) - spawnId = mSpawnId; - else - spawnId = BeingId_zero; - mSpawnId = BeingId_zero; - int16_t speed = msg.readInt16("speed"); - const uint16_t stunMode = msg.readInt16("opt1"); - uint32_t statusEffects = msg.readInt16("opt2"); - statusEffects |= (static_cast<uint32_t>(msg.readInt16("option"))) << 16; - const int16_t job = msg.readInt16("class"); - int disguiseId = 0; - if (id == localPlayer->getId() && job >= 1000) - disguiseId = job; - - Being *dstBeing = actorManager->findBeing(id); - - if (dstBeing && dstBeing->getType() == ActorType::Monster - && !dstBeing->isAlive()) - { - actorManager->destroy(dstBeing); - actorManager->erase(dstBeing); - dstBeing = nullptr; - } - - if (!dstBeing) - { - // Being with id >= 110000000 and job 0 are better - // known as ghosts, so don't create those. - if (job == 0 && toInt(id, int) >= 110000000) - { - BLOCK_END("BeingHandler::processBeingVisibleOrMove") - return; - } - - if (actorManager->isBlocked(id) == true) - { - BLOCK_END("BeingHandler::processBeingVisibleOrMove") - return; - } - - dstBeing = createBeing(id, job); - - if (!dstBeing) - { - BLOCK_END("BeingHandler::processBeingVisibleOrMove") - return; - } - } - else - { - if (dstBeing->getType() == ActorType::Npc) - { - actorManager->undelete(dstBeing); - if (serverVersion < 1) - beingHandler->requestNameById(id); - } - } - - if (dstBeing->getType() == ActorType::Player) - dstBeing->setMoveTime(); - - if (spawnId != BeingId_zero) - { - dstBeing->setAction(BeingAction::SPAWN, 0); - } - else - { - dstBeing->clearPath(); - dstBeing->setActionTime(tick_time); - dstBeing->setAction(BeingAction::STAND, 0); - } - - // Prevent division by 0 when calculating frame - if (speed == 0) - speed = 150; - - const uint8_t hairStyle = msg.readUInt8("hair style"); - const uint16_t look = msg.readUInt8("look"); - dstBeing->setSubtype(fromInt(job, BeingTypeId), look); - if (dstBeing->getType() == ActorType::Monster && localPlayer) - localPlayer->checkNewName(dstBeing); - dstBeing->setWalkSpeed(Vector(speed, speed, 0)); - const uint16_t weapon = msg.readInt16("weapon"); - const uint16_t headBottom = msg.readInt16("head bottom"); - - const uint16_t shield = msg.readInt16("shield"); - const uint16_t headTop = msg.readInt16("head top"); - const uint16_t headMid = msg.readInt16("head mid"); - const ItemColor hairColor = fromInt(msg.readUInt8("hair color"), ItemColor); - msg.readUInt8("unused"); - const uint16_t shoes = msg.readInt16("shoes / clothes color"); - - uint16_t gloves; - if (dstBeing->getType() == ActorType::Monster) - { - if (serverFeatures->haveServerHp()) - { - const int hp = msg.readInt32("hp"); - const int maxHP = msg.readInt32("max hp"); - if (hp && maxHP) - { - dstBeing->setMaxHP(maxHP); - const int oldHP = dstBeing->getHP(); - if (!oldHP || oldHP > hp) - dstBeing->setHP(hp); - } - } - else - { - msg.readInt32("unused"); - msg.readInt32("unused"); - } - gloves = 0; - } - else - { - gloves = msg.readInt16("gloves / head dir"); - msg.readInt32("guild"); - msg.readInt16("guild emblem"); - } - - dstBeing->setManner(msg.readInt16("manner")); - dstBeing->setStatusEffectBlock(32, msg.readInt16("opt3")); - if (serverFeatures->haveMonsterAttackRange() - && dstBeing->getType() == ActorType::Monster) - { - const int attackRange = static_cast<int>( - msg.readUInt8("attack range (was karma)")); - dstBeing->setAttackRange(attackRange); - } - else - { - dstBeing->setKarma(msg.readUInt8("karma")); - } - uint8_t gender = msg.readUInt8("gender"); - - if (!disguiseId && dstBeing->getType() == ActorType::Player) - { - // reserving bits for future usage - gender &= 3; - dstBeing->setGender(Being::intToGender(gender)); - // Set these after the gender, as the sprites may be gender-specific - setSprite(dstBeing, SPRITE_HAIR_COLOR, hairStyle * -1, - ItemDB::get(-hairStyle).getDyeColorsString(hairColor)); - dstBeing->setHairColor(hairColor); - setSprite(dstBeing, SPRITE_WEAPON, headBottom); - setSprite(dstBeing, SPRITE_HEAD_BOTTOM, headMid); - setSprite(dstBeing, SPRITE_CLOTHES_COLOR, headTop); - setSprite(dstBeing, SPRITE_HAIR, shoes); - setSprite(dstBeing, SPRITE_SHOES, gloves); - setSprite(dstBeing, SPRITE_BODY, weapon, "", ItemColor_one, true); - setSprite(dstBeing, SPRITE_FLOOR, shield); - } - else if (dstBeing->getType() == ActorType::Npc - && serverFeatures->haveNpcGender()) - { - setServerGender(dstBeing, gender); - } - - uint8_t dir; - uint16_t x, y; - msg.readCoordinates(x, y, dir, "position"); - dstBeing->setTileCoords(x, y); - - if (job == 45 && socialWindow && outfitWindow) - { - const int num = socialWindow->getPortalIndex(x, y); - if (num >= 0) - { - dstBeing->setName(keyboard.getKeyShortString( - outfitWindow->keyName(num))); - } - else - { - dstBeing->setName(""); - } - } - - dstBeing->setDirection(dir); - - msg.readUInt8("unknown"); - msg.readUInt8("unknown"); - msg.readUInt8("unknown"); - msg.readUInt8("unknown"); - msg.readUInt8("unknown"); - - dstBeing->setStunMode(stunMode); - dstBeing->setStatusEffectBlock(0, static_cast<uint16_t>( - (statusEffects >> 16) & 0xffff)); - dstBeing->setStatusEffectBlock(16, static_cast<uint16_t>( - statusEffects & 0xffff)); - BLOCK_END("BeingHandler::processBeingVisibleOrMove") -} - -void BeingHandler::processBeingMove(Net::MessageIn &msg) -{ - BLOCK_START("BeingHandler::processBeingVisibleOrMove") - if (!actorManager) - { - BLOCK_END("BeingHandler::processBeingVisibleOrMove") - return; - } - - BeingId spawnId; - - // Information about a being in range - const BeingId id = msg.readBeingId("being id"); - if (id == mSpawnId) - spawnId = mSpawnId; - else - spawnId = BeingId_zero; - mSpawnId = BeingId_zero; - int16_t speed = msg.readInt16("speed"); - const uint16_t stunMode = msg.readInt16("opt1"); - uint32_t statusEffects = msg.readInt16("opt2"); - statusEffects |= (static_cast<uint32_t>(msg.readInt16("option"))) << 16; - const int16_t job = msg.readInt16("class"); - int disguiseId = 0; - if (id == localPlayer->getId() && job >= 1000) - disguiseId = job; - - Being *dstBeing = actorManager->findBeing(id); - - if (dstBeing && dstBeing->getType() == ActorType::Monster - && !dstBeing->isAlive()) - { - actorManager->destroy(dstBeing); - actorManager->erase(dstBeing); - dstBeing = nullptr; - } - - if (!dstBeing) - { - // Being with id >= 110000000 and job 0 are better - // known as ghosts, so don't create those. - if (job == 0 && toInt(id, int) >= 110000000) - { - BLOCK_END("BeingHandler::processBeingVisibleOrMove") - return; - } - - if (actorManager->isBlocked(id) == true) - { - BLOCK_END("BeingHandler::processBeingVisibleOrMove") - return; - } - - dstBeing = createBeing(id, job); - - if (!dstBeing) - { - BLOCK_END("BeingHandler::processBeingVisibleOrMove") - return; - } - } - else - { - if (dstBeing->getType() == ActorType::Npc) - { - actorManager->undelete(dstBeing); - if (serverVersion < 1) - beingHandler->requestNameById(id); - } - } - - if (dstBeing->getType() == ActorType::Player) - dstBeing->setMoveTime(); - - if (spawnId != BeingId_zero) - dstBeing->setAction(BeingAction::SPAWN, 0); - - // Prevent division by 0 when calculating frame - if (speed == 0) - speed = 150; - - const uint8_t hairStyle = msg.readUInt8("hair style"); - const uint16_t look = msg.readUInt8("look"); - dstBeing->setSubtype(fromInt(job, BeingTypeId), look); - if (dstBeing->getType() == ActorType::Monster && localPlayer) - localPlayer->checkNewName(dstBeing); - dstBeing->setWalkSpeed(Vector(speed, speed, 0)); - const uint16_t weapon = msg.readInt16("weapon"); - const uint16_t headBottom = msg.readInt16("head bottom"); - - msg.readInt32("tick"); - - const uint16_t shield = msg.readInt16("shield"); - const uint16_t headTop = msg.readInt16("head top"); - const uint16_t headMid = msg.readInt16("head mid"); - const ItemColor hairColor = fromInt( - msg.readUInt8("hair color"), ItemColor); - msg.readUInt8("unused"); - const uint16_t shoes = msg.readInt16("shoes / clothes color"); - - uint16_t gloves; - if (dstBeing->getType() == ActorType::Monster) - { - if (serverFeatures->haveServerHp()) - { - const int hp = msg.readInt32("hp"); - const int maxHP = msg.readInt32("max hp"); - if (hp && maxHP) - { - dstBeing->setMaxHP(maxHP); - const int oldHP = dstBeing->getHP(); - if (!oldHP || oldHP > hp) - dstBeing->setHP(hp); - } - } - else - { - msg.readInt32("unused"); - msg.readInt32("unused"); - } - gloves = 0; - } - else - { - gloves = msg.readInt16("gloves / head dir"); - msg.readInt32("guild"); - msg.readInt16("guild emblem"); - } - - dstBeing->setManner(msg.readInt16("manner")); - dstBeing->setStatusEffectBlock(32, msg.readInt16("opt3")); - if (serverFeatures->haveMonsterAttackRange() - && dstBeing->getType() == ActorType::Monster) - { - const int attackRange = static_cast<int>( - msg.readUInt8("attack range (was karma)")); - dstBeing->setAttackRange(attackRange); - } - else - { - dstBeing->setKarma(msg.readUInt8("karma")); - } - uint8_t gender = msg.readUInt8("gender"); - - if (!disguiseId && dstBeing->getType() == ActorType::Player) - { - // reserving bits for future usage - gender &= 3; - dstBeing->setGender(Being::intToGender(gender)); - // Set these after the gender, as the sprites may be gender-specific - setSprite(dstBeing, SPRITE_HAIR_COLOR, hairStyle * -1, - ItemDB::get(-hairStyle).getDyeColorsString(hairColor)); - dstBeing->setHairColor(hairColor); - setSprite(dstBeing, SPRITE_WEAPON, headBottom); - setSprite(dstBeing, SPRITE_HEAD_BOTTOM, headMid); - setSprite(dstBeing, SPRITE_CLOTHES_COLOR, headTop); - setSprite(dstBeing, SPRITE_HAIR, shoes); - setSprite(dstBeing, SPRITE_SHOES, gloves); - setSprite(dstBeing, SPRITE_BODY, weapon, "", ItemColor_one, true); - setSprite(dstBeing, SPRITE_FLOOR, shield); - } - else if (dstBeing->getType() == ActorType::Npc - && serverFeatures->haveNpcGender()) - { - setServerGender(dstBeing, gender); - } - - uint16_t srcX, srcY, dstX, dstY; - msg.readCoordinatePair(srcX, srcY, dstX, dstY, "move path"); - if (!disguiseId) - { - dstBeing->setAction(BeingAction::STAND, 0); - dstBeing->setTileCoords(srcX, srcY); - if (!serverFeatures->haveMove3()) - dstBeing->setDestination(dstX, dstY); - } - - msg.readUInt8("unknown"); - msg.readUInt8("unknown"); - msg.readUInt8("unknown"); - msg.readUInt8("unknown"); - msg.readUInt8("unknown"); - - dstBeing->setStunMode(stunMode); - dstBeing->setStatusEffectBlock(0, static_cast<uint16_t>( - (statusEffects >> 16) & 0xffff)); - dstBeing->setStatusEffectBlock(16, static_cast<uint16_t>( - statusEffects & 0xffff)); - BLOCK_END("BeingHandler::processBeingVisibleOrMove") -} - -void BeingHandler::processBeingSpawn(Net::MessageIn &msg) -{ - BLOCK_START("BeingHandler::processBeingSpawn") - // skipping this packet - mSpawnId = msg.readBeingId("being id"); - msg.readInt16("speed"); - msg.readInt16("opt1"); - msg.readInt16("opt2"); - msg.readInt16("option"); - msg.readInt16("disguise"); - msg.skip(25, "unused"); - BLOCK_END("BeingHandler::processBeingSpawn") -} - -void BeingHandler::processSkillCasting(Net::MessageIn &msg) -{ - msg.readInt32("src id"); - msg.readInt32("dst id"); - msg.readInt16("dst x"); - msg.readInt16("dst y"); - msg.readInt16("skill num"); - msg.readInt32("skill get p1"); - msg.readInt32("cast time"); -} - -void BeingHandler::processBeingStatusChange(Net::MessageIn &msg) -{ - BLOCK_START("BeingHandler::processBeingStatusChange") - if (!actorManager) - { - BLOCK_END("BeingHandler::processBeingStatusChange") - return; - } - - // Status change - const uint16_t status = msg.readInt16("status"); - const BeingId id = msg.readBeingId("being id"); - const Enable flag = fromBool( - msg.readUInt8("flag: 0: stop, 1: start"), Enable); - - Being *const dstBeing = actorManager->findBeing(id); - if (dstBeing) - dstBeing->setStatusEffect(status, flag); - BLOCK_END("BeingHandler::processBeingStatusChange") -} - -void BeingHandler::processBeingMove2(Net::MessageIn &msg) -{ - BLOCK_START("BeingHandler::processBeingMove2") - if (!actorManager) - { - BLOCK_END("BeingHandler::processBeingMove2") - return; - } - - /* - * A simplified movement packet, used by the - * later versions of eAthena for both mobs and - * players - */ - Being *const dstBeing = actorManager->findBeing( - msg.readBeingId("being id")); - - /* - * This packet doesn't have enough info to actually - * create a new being, so if the being isn't found, - * we'll just pretend the packet didn't happen - */ - - if (!dstBeing) - { - BLOCK_END("BeingHandler::processBeingMove2") - return; - } - - uint16_t srcX, srcY, dstX, dstY; - msg.readCoordinatePair(srcX, srcY, dstX, dstY, "move path"); - msg.readInt32("tick"); - - dstBeing->setAction(BeingAction::STAND, 0); - dstBeing->setTileCoords(srcX, srcY); - dstBeing->setDestination(dstX, dstY); - if (dstBeing->getType() == ActorType::Player) - dstBeing->setMoveTime(); - BLOCK_END("BeingHandler::processBeingMove2") -} - -void BeingHandler::processBeingChangeDirection(Net::MessageIn &msg) -{ - BLOCK_START("BeingHandler::processBeingChangeDirection") - if (!actorManager) - { - BLOCK_END("BeingHandler::processBeingChangeDirection") - return; - } - - Being *const dstBeing = actorManager->findBeing( - msg.readBeingId("being id")); - - if (!dstBeing) - { - msg.readInt16("unused"); - msg.readUInt8("direction"); - BLOCK_END("BeingHandler::processBeingChangeDirection"); - return; - } - - msg.readInt16("unused"); - - const uint8_t dir = Net::MessageIn::fromServerDirection( - static_cast<uint8_t>(msg.readUInt8("direction") & 0x0FU)); - dstBeing->setDirection(dir); - if (localPlayer) - localPlayer->imitateDirection(dstBeing, dir); - BLOCK_END("BeingHandler::processBeingChangeDirection") -} - -void BeingHandler::setServerGender(Being *const being, const uint8_t gender) -{ - if (!being) - return; - switch (gender) - { - case 2: - being->setGender(Gender::FEMALE); - break; - case 3: - being->setGender(Gender::MALE); - break; - case 4: - being->setGender(Gender::OTHER); - break; - default: - being->setGender(Gender::UNSPECIFIED); - break; - } -} - -void BeingHandler::applyPlayerAction(Net::MessageIn &msg, - Being *const being, - const uint8_t type) -{ - if (!being) - return; - switch (type) - { - case 0: - being->setAction(BeingAction::STAND, 0); - localPlayer->imitateAction(being, BeingAction::STAND); - break; - - case 1: - if (being->getCurrentAction() != BeingAction::DEAD) - { - being->setAction(BeingAction::DEAD, 0); - being->recalcSpritesOrder(); - } - break; - - case 2: - being->setAction(BeingAction::SIT, 0); - localPlayer->imitateAction(being, BeingAction::SIT); - break; - - default: - UNIMPLIMENTEDPACKET; - break; - } -} - void BeingHandler::viewPlayerEquipment(const Being *const being A_UNUSED) { } -void BeingHandler::processPlaterStatusChange(Net::MessageIn &msg) -{ - BLOCK_START("BeingHandler::processPlayerStop") - if (!actorManager) - { - BLOCK_END("BeingHandler::processPlayerStop") - return; - } - - // Change in players' flags - const BeingId id = msg.readBeingId("account id"); - Being *const dstBeing = actorManager->findBeing(id); - if (!dstBeing) - return; - - const uint16_t stunMode = msg.readInt16("stun mode"); - uint32_t statusEffects = msg.readInt16("status effect"); - statusEffects |= (static_cast<uint32_t>(msg.readInt16("opt?"))) << 16; - msg.readUInt8("Unused?"); - - dstBeing->setStunMode(stunMode); - dstBeing->setStatusEffectBlock(0, static_cast<uint16_t>( - (statusEffects >> 16) & 0xffff)); - dstBeing->setStatusEffectBlock(16, static_cast<uint16_t>( - statusEffects & 0xffff)); - BLOCK_END("BeingHandler::processPlayerStop") -} - -void BeingHandler::processBeingResurrect(Net::MessageIn &msg) -{ - BLOCK_START("BeingHandler::processBeingResurrect") - if (!actorManager || !localPlayer) - { - BLOCK_END("BeingHandler::processBeingResurrect") - return; - } - - // A being changed mortality status - - const BeingId id = msg.readBeingId("being id"); - Being *const dstBeing = actorManager->findBeing(id); - if (!dstBeing) - { - BLOCK_END("BeingHandler::processBeingResurrect") - return; - } - - // If this is player's current target, clear it. - if (dstBeing == localPlayer->getTarget()) - localPlayer->stopAttack(); - - if (msg.readUInt8("flag?") == 1U) - dstBeing->setAction(BeingAction::STAND, 0); - BLOCK_END("BeingHandler::processBeingResurrect") -} - -void BeingHandler::processPlayerGuilPartyInfo(Net::MessageIn &msg) -{ - BLOCK_START("BeingHandler::processPlayerGuilPartyInfo") - if (!actorManager) - { - BLOCK_END("BeingHandler::processPlayerGuilPartyInfo") - return; - } - - Being *const dstBeing = actorManager->findBeing( - msg.readBeingId("being id")); - - if (dstBeing) - { - dstBeing->setPartyName(msg.readString(24, "party name")); - if (!guildManager || !GuildManager::getEnableGuildBot()) - { - dstBeing->setGuildName(msg.readString(24, "guild name")); - dstBeing->setGuildPos(msg.readString(24, "guild pos")); - } - else - { - msg.readString(24, "guild name"); - msg.readString(24, "guild pos"); - } - dstBeing->addToCache(); - msg.readString(24, "?"); - } - else - { - msg.readString(24, "party name"); - msg.readString(24, "guild name"); - msg.readString(24, "guild pos"); - msg.readString(24, "?"); - } - BLOCK_END("BeingHandler::processPlayerGuilPartyInfo") -} - -void BeingHandler::processBeingSelfEffect(Net::MessageIn &msg) -{ - BLOCK_START("BeingHandler::processBeingSelfEffect") - if (!effectManager || !actorManager) - { - BLOCK_END("BeingHandler::processBeingSelfEffect") - return; - } - - const BeingId id = msg.readBeingId("being id"); - Being *const being = actorManager->findBeing(id); - if (!being) - { - BLOCK_END("BeingHandler::processBeingSelfEffect") - return; - } - - const int effectType = msg.readInt32("effect type"); - - if (Particle::enabled) - effectManager->trigger(effectType, being); - - // +++ need dehard code effectType == 3 - if (effectType == 3 && being->getType() == ActorType::Player - && socialWindow) - { // reset received damage - socialWindow->resetDamage(being->getName()); - } - BLOCK_END("BeingHandler::processBeingSelfEffect") -} - -void BeingHandler::processSkillCastCancel(Net::MessageIn &msg) -{ - msg.readInt32("skill id"); -} - -void BeingHandler::processIpResponse(Net::MessageIn &msg) -{ - BLOCK_START("BeingHandler::processIpResponse") - if (!actorManager) - { - BLOCK_END("BeingHandler::processIpResponse") - return; - } - - Being *const dstBeing = actorManager->findBeing( - msg.readBeingId("being id")); - if (dstBeing) - dstBeing->setIp(ipToString(msg.readInt32("ip address"))); - BLOCK_END("BeingHandler::processIpResponse") -} - -void BeingHandler::processPvpSet(Net::MessageIn &msg) -{ - BLOCK_START("BeingHandler::processPvpSet") - const BeingId id = msg.readBeingId("being id"); - const int rank = msg.readInt32("rank"); - int teamId = 0; - if (serverFeatures->haveTeamId()) - teamId = msg.readInt32("team"); - else - msg.readInt32("num"); - if (actorManager) - { - Being *const dstBeing = actorManager->findBeing(id); - if (dstBeing) - { - dstBeing->setPvpRank(rank); - dstBeing->setTeamId(static_cast<uint16_t>(teamId)); - dstBeing->addToCache(); - } - } - BLOCK_END("BeingHandler::processPvpSet") -} - } // namespace TmwAthena diff --git a/src/net/tmwa/beinghandler.h b/src/net/tmwa/beinghandler.h index 1b722415d..a11415531 100644 --- a/src/net/tmwa/beinghandler.h +++ b/src/net/tmwa/beinghandler.h @@ -49,57 +49,6 @@ class BeingHandler final : public MessageHandler, public Ea::BeingHandler protected: void viewPlayerEquipment(const Being *const being); - - static void processBeingChangeLook(Net::MessageIn &msg); - - static void processBeingChangeLook2(Net::MessageIn &msg); - - static void processBeingVisible(Net::MessageIn &msg); - - static void processBeingMove(Net::MessageIn &msg); - - static void processPlayerUpdate1(Net::MessageIn &msg); - - static void processPlayerUpdate2(Net::MessageIn &msg); - - static void processPlayerMove(Net::MessageIn &msg); - - static void processBeingSpawn(Net::MessageIn &msg); - - static void processSkillCasting(Net::MessageIn &msg); - - static void processBeingStatusChange(Net::MessageIn &msg); - - static void processBeingMove2(Net::MessageIn &msg); - - static void processBeingChangeDirection(Net::MessageIn &msg); - - static void processBeingChangeLookContinue(Net::MessageIn &msg, - Being *const dstBeing, - const uint8_t type, - const int id, - const int id2) A_NONNULL(2); - - static void processPlaterStatusChange(Net::MessageIn &msg); - - static void processBeingResurrect(Net::MessageIn &msg); - - static void processPlayerGuilPartyInfo(Net::MessageIn &msg); - - static void setServerGender(Being *const being, - const uint8_t gender); - - static void applyPlayerAction(Net::MessageIn &msg, - Being *const being, - const uint8_t type); - - static void processBeingSelfEffect(Net::MessageIn &msg); - - static void processSkillCastCancel(Net::MessageIn &msg); - - static void processIpResponse(Net::MessageIn &msg); - - static void processPvpSet(Net::MessageIn &msg); }; } // namespace TmwAthena diff --git a/src/net/tmwa/beingnet.cpp b/src/net/tmwa/beingnet.cpp new file mode 100644 index 000000000..0230e053d --- /dev/null +++ b/src/net/tmwa/beingnet.cpp @@ -0,0 +1,1391 @@ +/* + * The ManaPlus Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011-2015 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "net/tmwa/beingnet.h" + +#include "actormanager.h" +#include "effectmanager.h" +#include "guild.h" +#include "party.h" + +#include "being/localplayer.h" + +#include "particle/particle.h" + +#include "input/keyboardconfig.h" + +#include "gui/windows/outfitwindow.h" +#include "gui/windows/socialwindow.h" + +#include "net/serverfeatures.h" + +#include "net/ea/beingnet.h" + +#include "net/tmwa/guildmanager.h" +#include "net/tmwa/messageout.h" +#include "net/tmwa/protocol.h" +#include "net/tmwa/sprite.h" + +#include "resources/iteminfo.h" + +#include "resources/db/itemdb.h" + +#include "utils/stringutils.h" +#include "utils/timer.h" + +#include "debug.h" + +extern int serverVersion; + +namespace TmwAthena +{ + +void BeingNet::processBeingChangeLook(Net::MessageIn &msg) +{ + BLOCK_START("BeingNet::processBeingChangeLook") + if (!actorManager) + { + BLOCK_END("BeingNet::processBeingChangeLook") + return; + } + + Being *const dstBeing = actorManager->findBeing( + msg.readBeingId("being id")); + + const uint8_t type = msg.readUInt8("type"); + const int16_t id = static_cast<int16_t>(msg.readUInt8("id")); + const int id2 = 1; + + if (!localPlayer || !dstBeing) + { + BLOCK_END("BeingNet::processBeingChangeLook") + return; + } + processBeingChangeLookContinue(msg, dstBeing, type, id, id2); + BLOCK_END("BeingNet::processBeingChangeLook") +} + +void BeingNet::processBeingChangeLook2(Net::MessageIn &msg) +{ + BLOCK_START("BeingNet::processBeingChangeLook") + if (!actorManager) + { + BLOCK_END("BeingNet::processBeingChangeLook") + return; + } + + Being *const dstBeing = actorManager->findBeing( + msg.readBeingId("being id")); + + const uint8_t type = msg.readUInt8("type"); + int id2 = 0; + + const int16_t id = msg.readInt16("id1"); + if (type == 2) + { + id2 = msg.readInt16("id2"); + } + else + { + msg.readInt16("id2"); + id2 = 1; + } + + if (!localPlayer || !dstBeing) + { + BLOCK_END("BeingNet::processBeingChangeLook") + return; + } + processBeingChangeLookContinue(msg, dstBeing, type, id, id2); + BLOCK_END("BeingNet::processBeingChangeLook") +} + +void BeingNet::processBeingChangeLookContinue(Net::MessageIn &msg, + Being *const dstBeing, + const uint8_t type, + const int id, + const int id2) +{ + if (dstBeing->getType() == ActorType::Player) + dstBeing->setOtherTime(); + + const std::string color; + switch (type) + { + case 0: // change race + dstBeing->setSubtype(fromInt(id, BeingTypeId), + dstBeing->getLook()); + break; + case 1: // eAthena LOOK_HAIR + { + const uint16_t look = static_cast<uint16_t>(id / 256); + const int hair = id % 256; + dstBeing->setHairStyle(SPRITE_HAIR_COLOR, hair * -1); + dstBeing->setLook(look); + break; + } + case 2: // Weapon ID in id, Shield ID in id2 + dstBeing->setSprite(SPRITE_BODY, id, "", ItemColor_one, true); + dstBeing->setSprite(SPRITE_FLOOR, id2); + localPlayer->imitateOutfit(dstBeing, SPRITE_FLOOR); + break; + case 3: // Change lower headgear for eAthena, pants for us + dstBeing->setSprite(SPRITE_WEAPON, id, color, + ItemColor_one); + localPlayer->imitateOutfit(dstBeing, SPRITE_WEAPON); + break; + case 4: // Change upper headgear for eAthena, hat for us + dstBeing->setSprite(SPRITE_CLOTHES_COLOR, id, color, + ItemColor_one); + localPlayer->imitateOutfit(dstBeing, SPRITE_CLOTHES_COLOR); + break; + case 5: // Change middle headgear for eathena, armor for us + dstBeing->setSprite(SPRITE_HEAD_BOTTOM, id, color, + ItemColor_one); + localPlayer->imitateOutfit(dstBeing, SPRITE_HEAD_BOTTOM); + break; + case 6: // eAthena LOOK_HAIR_COLOR + dstBeing->setHairColor(SPRITE_HAIR_COLOR, + fromInt(id, ItemColor)); + break; + case 7: // Clothes color + // ignoring it + break; + case 8: // eAthena LOOK_SHIELD + dstBeing->setSprite(SPRITE_FLOOR, id, color, + ItemColor_one); + localPlayer->imitateOutfit(dstBeing, SPRITE_FLOOR); + break; + case 9: // eAthena LOOK_SHOES + dstBeing->setSprite(SPRITE_HAIR, id, color, + ItemColor_one); + localPlayer->imitateOutfit(dstBeing, SPRITE_HAIR); + break; + case 10: // LOOK_GLOVES + dstBeing->setSprite(SPRITE_SHOES, id, color, + ItemColor_one); + localPlayer->imitateOutfit(dstBeing, SPRITE_SHOES); + break; + case 11: // LOOK_CAPE + dstBeing->setSprite(SPRITE_SHIELD, id, color, + ItemColor_one); + localPlayer->imitateOutfit(dstBeing, SPRITE_SHIELD); + break; + case 12: + dstBeing->setSprite(SPRITE_HEAD_TOP, id, color, + ItemColor_one); + localPlayer->imitateOutfit(dstBeing, SPRITE_HEAD_TOP); + break; + case 13: + dstBeing->setSprite(SPRITE_HEAD_MID, id, color, + ItemColor_one); + localPlayer->imitateOutfit(dstBeing, SPRITE_HEAD_MID); + break; + case 14: + dstBeing->setSprite(SPRITE_ROBE, id, color, + ItemColor_one); + localPlayer->imitateOutfit(dstBeing, SPRITE_ROBE); + break; + case 15: + dstBeing->setSprite(SPRITE_EVOL2, id, color, + ItemColor_one); + localPlayer->imitateOutfit(dstBeing, SPRITE_EVOL2); + break; + case 16: + dstBeing->setLook(static_cast<uint16_t>(id)); + break; + default: + UNIMPLIMENTEDPACKET; + break; + } +} + +void BeingNet::processPlayerUpdate1(Net::MessageIn &msg) +{ + BLOCK_START("BeingNet::processPlayerMoveUpdate") + if (!actorManager || !localPlayer) + { + BLOCK_END("BeingNet::processPlayerMoveUpdate") + return; + } + + // An update about a player, potentially including movement. + const BeingId id = msg.readBeingId("account id"); + const int16_t speed = msg.readInt16("speed"); + const uint16_t stunMode = msg.readInt16("opt1"); + uint32_t statusEffects = msg.readInt16("opt2"); + statusEffects |= (static_cast<uint32_t>(msg.readInt16("options"))) + << 16; + const int16_t job = msg.readInt16("job"); + int disguiseId = 0; + if (toInt(id, int) < 110000000 && job >= 1000) + disguiseId = job; + + Being *dstBeing = actorManager->findBeing(id); + if (!dstBeing) + { + if (actorManager->isBlocked(id) == true) + { + BLOCK_END("BeingNet::processPlayerMoveUpdate") + return; + } + + dstBeing = Ea::BeingNet::createBeing(id, job); + + if (!dstBeing) + { + BLOCK_END("BeingNet::processPlayerMoveUpdate") + return; + } + } + else if (disguiseId) + { + actorManager->undelete(dstBeing); + if (serverVersion < 1) + beingHandler->requestNameById(id); + } + + uint8_t dir = dstBeing->getDirectionDelayed(); + if (dir) + { + if (dir != dstBeing->getDirection()) + dstBeing->setDirection(dir); + } + + if (Party *const party = localPlayer->getParty()) + { + if (party->isMember(id)) + dstBeing->setParty(party); + } + + dstBeing->setWalkSpeed(Vector(speed, speed, 0)); + + const uint8_t hairStyle = msg.readUInt8("hair style"); + const uint16_t look = msg.readUInt8("look"); + dstBeing->setSubtype(fromInt(job, BeingTypeId), look); + const uint16_t weapon = msg.readInt16("weapon"); + const uint16_t shield = msg.readInt16("shield"); + const uint16_t headBottom = msg.readInt16("head bottom"); + + const uint16_t headTop = msg.readInt16("head top"); + const uint16_t headMid = msg.readInt16("head mid"); + const ItemColor hairColor = fromInt( + msg.readUInt8("hair color"), ItemColor); + msg.readUInt8("unused"); + msg.readInt32("unused"); + + const int guild = msg.readInt32("guild"); + + if (!guildManager || !GuildManager::getEnableGuildBot()) + { + if (guild == 0) + dstBeing->clearGuilds(); + else + dstBeing->setGuild(Guild::getGuild(static_cast<int16_t>(guild))); + } + + msg.readInt16("emblem"); + dstBeing->setManner(msg.readInt16("manner")); + dstBeing->setStatusEffectBlock(32, msg.readInt16("opt3")); + dstBeing->setKarma(msg.readUInt8("karma")); + // reserving bit for future usage + dstBeing->setGender(Being::intToGender( + static_cast<uint8_t>(msg.readUInt8("gender") & 3))); + + if (!disguiseId) + { + // Set these after the gender, as the sprites may be gender-specific + dstBeing->updateSprite(SPRITE_BODY, weapon, "", ItemColor_one, true); + dstBeing->updateSprite(SPRITE_FLOOR, shield); + dstBeing->updateSprite(SPRITE_WEAPON, headBottom); + dstBeing->updateSprite(SPRITE_HEAD_BOTTOM, headMid); + dstBeing->updateSprite(SPRITE_CLOTHES_COLOR, headTop); + dstBeing->updateSprite(SPRITE_HAIR_COLOR, hairStyle * -1, + ItemDB::get(-hairStyle).getDyeColorsString(hairColor)); + dstBeing->setHairColor(hairColor); + } + localPlayer->imitateOutfit(dstBeing); + + uint16_t x, y; + msg.readCoordinates(x, y, dir, "position"); + dstBeing->setTileCoords(x, y); + dstBeing->setDirection(dir); + + localPlayer->imitateDirection(dstBeing, dir); + + const uint16_t gmstatus = msg.readInt16("gm status"); + + if (gmstatus & 0x80) + dstBeing->setGM(true); + + applyPlayerAction(msg, dstBeing, msg.readUInt8("action type")); + const int level = static_cast<int>(msg.readUInt8("level")); + if (level) + dstBeing->setLevel(level); + + msg.readUInt8("unused"); + + dstBeing->setActionTime(tick_time); + + dstBeing->setStunMode(stunMode); + dstBeing->setStatusEffectBlock(0, static_cast<uint16_t>( + (statusEffects >> 16) & 0xffff)); + dstBeing->setStatusEffectBlock(16, static_cast<uint16_t>( + statusEffects & 0xffff)); + + BLOCK_END("BeingNet::processPlayerMoveUpdate") +} + +void BeingNet::processPlayerUpdate2(Net::MessageIn &msg) +{ + BLOCK_START("BeingNet::processPlayerMoveUpdate") + if (!actorManager || !localPlayer) + { + BLOCK_END("BeingNet::processPlayerMoveUpdate") + return; + } + + // An update about a player, potentially including movement. + const BeingId id = msg.readBeingId("account id"); + const int16_t speed = msg.readInt16("speed"); + const uint16_t stunMode = msg.readInt16("opt1"); + uint32_t statusEffects = msg.readInt16("opt2"); + statusEffects |= (static_cast<uint32_t>(msg.readInt16("options"))) + << 16; + const int16_t job = msg.readInt16("job"); + int disguiseId = 0; + if (toInt(id, int) < 110000000 && job >= 1000) + disguiseId = job; + + Being *dstBeing = actorManager->findBeing(id); + if (!dstBeing) + { + if (actorManager->isBlocked(id) == true) + { + BLOCK_END("BeingNet::processPlayerMoveUpdate") + return; + } + + dstBeing = Ea::BeingNet::createBeing(id, job); + + if (!dstBeing) + { + BLOCK_END("BeingNet::processPlayerMoveUpdate") + return; + } + } + else if (disguiseId) + { + actorManager->undelete(dstBeing); + if (serverVersion < 1) + beingHandler->requestNameById(id); + } + + uint8_t dir = dstBeing->getDirectionDelayed(); + if (dir) + { + if (dir != dstBeing->getDirection()) + dstBeing->setDirection(dir); + } + + if (Party *const party = localPlayer->getParty()) + { + if (party->isMember(id)) + dstBeing->setParty(party); + } + + dstBeing->setWalkSpeed(Vector(speed, speed, 0)); + + const uint8_t hairStyle = msg.readUInt8("hair style"); + const uint16_t look = msg.readUInt8("look"); + dstBeing->setSubtype(fromInt(job, BeingTypeId), look); + const uint16_t weapon = msg.readInt16("weapon"); + const uint16_t shield = msg.readInt16("shield"); + const uint16_t headBottom = msg.readInt16("head bottom"); + const uint16_t headTop = msg.readInt16("head top"); + const uint16_t headMid = msg.readInt16("head mid"); + const ItemColor hairColor = fromInt( + msg.readUInt8("hair color"), ItemColor); + msg.readUInt8("unused"); + msg.readInt32("unused"); + + const int guild = msg.readInt32("guild"); + + if (!guildManager || !GuildManager::getEnableGuildBot()) + { + if (guild == 0) + dstBeing->clearGuilds(); + else + dstBeing->setGuild(Guild::getGuild(static_cast<int16_t>(guild))); + } + + msg.readInt16("emblem"); + dstBeing->setManner(msg.readInt16("manner")); + dstBeing->setStatusEffectBlock(32, msg.readInt16("opt3")); + dstBeing->setKarma(msg.readUInt8("karma")); + // reserving bit for future usage + dstBeing->setGender(Being::intToGender( + static_cast<uint8_t>(msg.readUInt8("gender") & 3))); + + if (!disguiseId) + { + // Set these after the gender, as the sprites may be gender-specific + dstBeing->updateSprite(SPRITE_BODY, weapon, "", ItemColor_one, true); + dstBeing->updateSprite(SPRITE_FLOOR, shield); + dstBeing->updateSprite(SPRITE_WEAPON, headBottom); + dstBeing->updateSprite(SPRITE_HEAD_BOTTOM, headMid); + dstBeing->updateSprite(SPRITE_CLOTHES_COLOR, headTop); + dstBeing->updateSprite(SPRITE_HAIR_COLOR, hairStyle * -1, + ItemDB::get(-hairStyle).getDyeColorsString(hairColor)); + dstBeing->setHairColor(hairColor); + } + localPlayer->imitateOutfit(dstBeing); + + uint16_t x, y; + msg.readCoordinates(x, y, dir, "position"); + dstBeing->setTileCoords(x, y); + dstBeing->setDirection(dir); + + localPlayer->imitateDirection(dstBeing, dir); + + const uint16_t gmstatus = msg.readInt16("gm status"); + + if (gmstatus & 0x80) + dstBeing->setGM(true); + + applyPlayerAction(msg, dstBeing, msg.readUInt8("action type")); + const int level = static_cast<int>(msg.readUInt8("level")); + if (level) + dstBeing->setLevel(level); + + dstBeing->setActionTime(tick_time); + dstBeing->setStunMode(stunMode); + dstBeing->setStatusEffectBlock(0, static_cast<uint16_t>( + (statusEffects >> 16) & 0xffff)); + dstBeing->setStatusEffectBlock(16, static_cast<uint16_t>( + statusEffects & 0xffff)); + + BLOCK_END("BeingNet::processPlayerMoveUpdate") +} + +void BeingNet::processPlayerMove(Net::MessageIn &msg) +{ + BLOCK_START("BeingNet::processPlayerMoveUpdate") + if (!actorManager || !localPlayer) + { + BLOCK_END("BeingNet::processPlayerMoveUpdate") + return; + } + + // An update about a player, potentially including movement. + const BeingId id = msg.readBeingId("account id"); + const int16_t speed = msg.readInt16("speed"); + const uint16_t stunMode = msg.readInt16("opt1"); + uint32_t statusEffects = msg.readInt16("opt2"); + statusEffects |= (static_cast<uint32_t>(msg.readInt16("options"))) + << 16; + const int16_t job = msg.readInt16("job"); + int disguiseId = 0; + if (toInt(id, int) < 110000000 && job >= 1000) + disguiseId = job; + + Being *dstBeing = actorManager->findBeing(id); + if (!dstBeing) + { + if (actorManager->isBlocked(id) == true) + { + BLOCK_END("BeingNet::processPlayerMoveUpdate") + return; + } + + dstBeing = Ea::BeingNet::createBeing(id, job); + + if (!dstBeing) + { + BLOCK_END("BeingNet::processPlayerMoveUpdate") + return; + } + } + else if (disguiseId) + { + actorManager->undelete(dstBeing); + if (serverVersion < 1) + beingHandler->requestNameById(id); + } + + const uint8_t dir = dstBeing->getDirectionDelayed(); + if (dir) + { + if (dir != dstBeing->getDirection()) + dstBeing->setDirection(dir); + } + + if (Party *const party = localPlayer->getParty()) + { + if (party->isMember(id)) + dstBeing->setParty(party); + } + + dstBeing->setWalkSpeed(Vector(speed, speed, 0)); + + const uint8_t hairStyle = msg.readUInt8("hair style"); + const uint16_t look = msg.readUInt8("look"); + dstBeing->setSubtype(fromInt(job, BeingTypeId), look); + const uint16_t weapon = msg.readInt16("weapon"); + const uint16_t shield = msg.readInt16("shield"); + const uint16_t headBottom = msg.readInt16("head bottom"); + + msg.readInt32("tick"); + + const uint16_t headTop = msg.readInt16("head top"); + const uint16_t headMid = msg.readInt16("head mid"); + const ItemColor hairColor = fromInt( + msg.readUInt8("hair color"), ItemColor); + msg.readUInt8("unused"); + msg.readInt32("unused"); + + const int guild = msg.readInt32("guild"); + + if (!guildManager || !GuildManager::getEnableGuildBot()) + { + if (guild == 0) + dstBeing->clearGuilds(); + else + dstBeing->setGuild(Guild::getGuild(static_cast<int16_t>(guild))); + } + + msg.readInt16("emblem"); + dstBeing->setManner(msg.readInt16("manner")); + dstBeing->setStatusEffectBlock(32, msg.readInt16("opt3")); + dstBeing->setKarma(msg.readUInt8("karma")); + // reserving bit for future usage + dstBeing->setGender(Being::intToGender( + static_cast<uint8_t>(msg.readUInt8("gender") & 3))); + + if (!disguiseId) + { + // Set these after the gender, as the sprites may be gender-specific + dstBeing->updateSprite(SPRITE_BODY, weapon, "", ItemColor_one, true); + dstBeing->updateSprite(SPRITE_FLOOR, shield); + dstBeing->updateSprite(SPRITE_WEAPON, headBottom); + dstBeing->updateSprite(SPRITE_HEAD_BOTTOM, headMid); + dstBeing->updateSprite(SPRITE_CLOTHES_COLOR, headTop); + dstBeing->updateSprite(SPRITE_HAIR_COLOR, hairStyle * -1, + ItemDB::get(-hairStyle).getDyeColorsString(hairColor)); + dstBeing->setHairColor(hairColor); + } + localPlayer->imitateOutfit(dstBeing); + + uint16_t srcX, srcY, dstX, dstY; + msg.readCoordinatePair(srcX, srcY, dstX, dstY, "moving path"); + + localPlayer->followMoveTo(dstBeing, srcX, srcY, dstX, dstY); + + dstBeing->setTileCoords(srcX, srcY); + dstBeing->setDestination(dstX, dstY); + + // because server don't send direction in move packet, + // we fixing it + + if (srcX != dstX || srcY != dstY) + { + const int d = dstBeing->calcDirection(dstX, dstY); + + if (d && dstBeing->getDirection() != d) + dstBeing->setDirectionDelayed(static_cast<uint8_t>(d)); + } + + if (localPlayer->getCurrentAction() != BeingAction::STAND) + localPlayer->imitateAction(dstBeing, BeingAction::STAND); + if (localPlayer->getDirection() != dstBeing->getDirection()) + { + localPlayer->imitateDirection(dstBeing, + dstBeing->getDirection()); + } + + const uint16_t gmstatus = msg.readInt16("gm status"); + + if (gmstatus & 0x80) + dstBeing->setGM(true); + + msg.readUInt8("unused"); + + const int level = static_cast<int>(msg.readUInt8("level")); + if (level) + dstBeing->setLevel(level); + + msg.readUInt8("unused"); + + if (dstBeing->getType() != ActorType::Player) + dstBeing->setActionTime(tick_time); + + dstBeing->setStunMode(stunMode); + dstBeing->setStatusEffectBlock(0, static_cast<uint16_t>( + (statusEffects >> 16) & 0xffff)); + dstBeing->setStatusEffectBlock(16, static_cast<uint16_t>( + statusEffects & 0xffff)); + + if (dstBeing->getType() == ActorType::Player) + dstBeing->setMoveTime(); + BLOCK_END("BeingNet::processPlayerMoveUpdate") +} + +void BeingNet::processBeingVisible(Net::MessageIn &msg) +{ + BLOCK_START("BeingNet::processBeingVisibleOrMove") + if (!actorManager) + { + BLOCK_END("BeingNet::processBeingVisibleOrMove") + return; + } + + BeingId spawnId; + + // Information about a being in range + const BeingId id = msg.readBeingId("being id"); + if (id == Ea::BeingNet::mSpawnId) + spawnId = Ea::BeingNet::mSpawnId; + else + spawnId = BeingId_zero; + Ea::BeingNet::mSpawnId = BeingId_zero; + int16_t speed = msg.readInt16("speed"); + const uint16_t stunMode = msg.readInt16("opt1"); + uint32_t statusEffects = msg.readInt16("opt2"); + statusEffects |= (static_cast<uint32_t>(msg.readInt16("option"))) << 16; + const int16_t job = msg.readInt16("class"); + int disguiseId = 0; + if (id == localPlayer->getId() && job >= 1000) + disguiseId = job; + + Being *dstBeing = actorManager->findBeing(id); + + if (dstBeing && dstBeing->getType() == ActorType::Monster + && !dstBeing->isAlive()) + { + actorManager->destroy(dstBeing); + actorManager->erase(dstBeing); + dstBeing = nullptr; + } + + if (!dstBeing) + { + // Being with id >= 110000000 and job 0 are better + // known as ghosts, so don't create those. + if (job == 0 && toInt(id, int) >= 110000000) + { + BLOCK_END("BeingNet::processBeingVisibleOrMove") + return; + } + + if (actorManager->isBlocked(id) == true) + { + BLOCK_END("BeingNet::processBeingVisibleOrMove") + return; + } + + dstBeing = Ea::BeingNet::createBeing(id, job); + + if (!dstBeing) + { + BLOCK_END("BeingNet::processBeingVisibleOrMove") + return; + } + } + else + { + if (dstBeing->getType() == ActorType::Npc) + { + actorManager->undelete(dstBeing); + if (serverVersion < 1) + beingHandler->requestNameById(id); + } + } + + if (dstBeing->getType() == ActorType::Player) + dstBeing->setMoveTime(); + + if (spawnId != BeingId_zero) + { + dstBeing->setAction(BeingAction::SPAWN, 0); + } + else + { + dstBeing->clearPath(); + dstBeing->setActionTime(tick_time); + dstBeing->setAction(BeingAction::STAND, 0); + } + + // Prevent division by 0 when calculating frame + if (speed == 0) + speed = 150; + + const uint8_t hairStyle = msg.readUInt8("hair style"); + const uint16_t look = msg.readUInt8("look"); + dstBeing->setSubtype(fromInt(job, BeingTypeId), look); + if (dstBeing->getType() == ActorType::Monster && localPlayer) + localPlayer->checkNewName(dstBeing); + dstBeing->setWalkSpeed(Vector(speed, speed, 0)); + const uint16_t weapon = msg.readInt16("weapon"); + const uint16_t headBottom = msg.readInt16("head bottom"); + + const uint16_t shield = msg.readInt16("shield"); + const uint16_t headTop = msg.readInt16("head top"); + const uint16_t headMid = msg.readInt16("head mid"); + const ItemColor hairColor = fromInt(msg.readUInt8("hair color"), ItemColor); + msg.readUInt8("unused"); + const uint16_t shoes = msg.readInt16("shoes / clothes color"); + + uint16_t gloves; + if (dstBeing->getType() == ActorType::Monster) + { + if (serverFeatures->haveServerHp()) + { + const int hp = msg.readInt32("hp"); + const int maxHP = msg.readInt32("max hp"); + if (hp && maxHP) + { + dstBeing->setMaxHP(maxHP); + const int oldHP = dstBeing->getHP(); + if (!oldHP || oldHP > hp) + dstBeing->setHP(hp); + } + } + else + { + msg.readInt32("unused"); + msg.readInt32("unused"); + } + gloves = 0; + } + else + { + gloves = msg.readInt16("gloves / head dir"); + msg.readInt32("guild"); + msg.readInt16("guild emblem"); + } + + dstBeing->setManner(msg.readInt16("manner")); + dstBeing->setStatusEffectBlock(32, msg.readInt16("opt3")); + if (serverFeatures->haveMonsterAttackRange() + && dstBeing->getType() == ActorType::Monster) + { + const int attackRange = static_cast<int>( + msg.readUInt8("attack range (was karma)")); + dstBeing->setAttackRange(attackRange); + } + else + { + dstBeing->setKarma(msg.readUInt8("karma")); + } + uint8_t gender = msg.readUInt8("gender"); + + if (!disguiseId && dstBeing->getType() == ActorType::Player) + { + // reserving bits for future usage + gender &= 3; + dstBeing->setGender(Being::intToGender(gender)); + // Set these after the gender, as the sprites may be gender-specific + Ea::BeingNet::setSprite(dstBeing, SPRITE_HAIR_COLOR, hairStyle * -1, + ItemDB::get(-hairStyle).getDyeColorsString(hairColor)); + dstBeing->setHairColor(hairColor); + Ea::BeingNet::setSprite(dstBeing, SPRITE_WEAPON, headBottom); + Ea::BeingNet::setSprite(dstBeing, SPRITE_HEAD_BOTTOM, headMid); + Ea::BeingNet::setSprite(dstBeing, SPRITE_CLOTHES_COLOR, headTop); + Ea::BeingNet::setSprite(dstBeing, SPRITE_HAIR, shoes); + Ea::BeingNet::setSprite(dstBeing, SPRITE_SHOES, gloves); + Ea::BeingNet::setSprite(dstBeing, SPRITE_BODY, weapon, "", + ItemColor_one, true); + Ea::BeingNet::setSprite(dstBeing, SPRITE_FLOOR, shield); + } + else if (dstBeing->getType() == ActorType::Npc + && serverFeatures->haveNpcGender()) + { + setServerGender(dstBeing, gender); + } + + uint8_t dir; + uint16_t x, y; + msg.readCoordinates(x, y, dir, "position"); + dstBeing->setTileCoords(x, y); + + if (job == 45 && socialWindow && outfitWindow) + { + const int num = socialWindow->getPortalIndex(x, y); + if (num >= 0) + { + dstBeing->setName(keyboard.getKeyShortString( + outfitWindow->keyName(num))); + } + else + { + dstBeing->setName(""); + } + } + + dstBeing->setDirection(dir); + + msg.readUInt8("unknown"); + msg.readUInt8("unknown"); + msg.readUInt8("unknown"); + msg.readUInt8("unknown"); + msg.readUInt8("unknown"); + + dstBeing->setStunMode(stunMode); + dstBeing->setStatusEffectBlock(0, static_cast<uint16_t>( + (statusEffects >> 16) & 0xffff)); + dstBeing->setStatusEffectBlock(16, static_cast<uint16_t>( + statusEffects & 0xffff)); + BLOCK_END("BeingNet::processBeingVisibleOrMove") +} + +void BeingNet::processBeingMove(Net::MessageIn &msg) +{ + BLOCK_START("BeingNet::processBeingVisibleOrMove") + if (!actorManager) + { + BLOCK_END("BeingNet::processBeingVisibleOrMove") + return; + } + + BeingId spawnId; + + // Information about a being in range + const BeingId id = msg.readBeingId("being id"); + if (id == Ea::BeingNet::mSpawnId) + spawnId = Ea::BeingNet::mSpawnId; + else + spawnId = BeingId_zero; + Ea::BeingNet::mSpawnId = BeingId_zero; + int16_t speed = msg.readInt16("speed"); + const uint16_t stunMode = msg.readInt16("opt1"); + uint32_t statusEffects = msg.readInt16("opt2"); + statusEffects |= (static_cast<uint32_t>(msg.readInt16("option"))) << 16; + const int16_t job = msg.readInt16("class"); + int disguiseId = 0; + if (id == localPlayer->getId() && job >= 1000) + disguiseId = job; + + Being *dstBeing = actorManager->findBeing(id); + + if (dstBeing && dstBeing->getType() == ActorType::Monster + && !dstBeing->isAlive()) + { + actorManager->destroy(dstBeing); + actorManager->erase(dstBeing); + dstBeing = nullptr; + } + + if (!dstBeing) + { + // Being with id >= 110000000 and job 0 are better + // known as ghosts, so don't create those. + if (job == 0 && toInt(id, int) >= 110000000) + { + BLOCK_END("BeingNet::processBeingVisibleOrMove") + return; + } + + if (actorManager->isBlocked(id) == true) + { + BLOCK_END("BeingNet::processBeingVisibleOrMove") + return; + } + + dstBeing = Ea::BeingNet::createBeing(id, job); + + if (!dstBeing) + { + BLOCK_END("BeingNet::processBeingVisibleOrMove") + return; + } + } + else + { + if (dstBeing->getType() == ActorType::Npc) + { + actorManager->undelete(dstBeing); + if (serverVersion < 1) + beingHandler->requestNameById(id); + } + } + + if (dstBeing->getType() == ActorType::Player) + dstBeing->setMoveTime(); + + if (spawnId != BeingId_zero) + dstBeing->setAction(BeingAction::SPAWN, 0); + + // Prevent division by 0 when calculating frame + if (speed == 0) + speed = 150; + + const uint8_t hairStyle = msg.readUInt8("hair style"); + const uint16_t look = msg.readUInt8("look"); + dstBeing->setSubtype(fromInt(job, BeingTypeId), look); + if (dstBeing->getType() == ActorType::Monster && localPlayer) + localPlayer->checkNewName(dstBeing); + dstBeing->setWalkSpeed(Vector(speed, speed, 0)); + const uint16_t weapon = msg.readInt16("weapon"); + const uint16_t headBottom = msg.readInt16("head bottom"); + + msg.readInt32("tick"); + + const uint16_t shield = msg.readInt16("shield"); + const uint16_t headTop = msg.readInt16("head top"); + const uint16_t headMid = msg.readInt16("head mid"); + const ItemColor hairColor = fromInt( + msg.readUInt8("hair color"), ItemColor); + msg.readUInt8("unused"); + const uint16_t shoes = msg.readInt16("shoes / clothes color"); + + uint16_t gloves; + if (dstBeing->getType() == ActorType::Monster) + { + if (serverFeatures->haveServerHp()) + { + const int hp = msg.readInt32("hp"); + const int maxHP = msg.readInt32("max hp"); + if (hp && maxHP) + { + dstBeing->setMaxHP(maxHP); + const int oldHP = dstBeing->getHP(); + if (!oldHP || oldHP > hp) + dstBeing->setHP(hp); + } + } + else + { + msg.readInt32("unused"); + msg.readInt32("unused"); + } + gloves = 0; + } + else + { + gloves = msg.readInt16("gloves / head dir"); + msg.readInt32("guild"); + msg.readInt16("guild emblem"); + } + + dstBeing->setManner(msg.readInt16("manner")); + dstBeing->setStatusEffectBlock(32, msg.readInt16("opt3")); + if (serverFeatures->haveMonsterAttackRange() + && dstBeing->getType() == ActorType::Monster) + { + const int attackRange = static_cast<int>( + msg.readUInt8("attack range (was karma)")); + dstBeing->setAttackRange(attackRange); + } + else + { + dstBeing->setKarma(msg.readUInt8("karma")); + } + uint8_t gender = msg.readUInt8("gender"); + + if (!disguiseId && dstBeing->getType() == ActorType::Player) + { + // reserving bits for future usage + gender &= 3; + dstBeing->setGender(Being::intToGender(gender)); + // Set these after the gender, as the sprites may be gender-specific + Ea::BeingNet::setSprite(dstBeing, SPRITE_HAIR_COLOR, hairStyle * -1, + ItemDB::get(-hairStyle).getDyeColorsString(hairColor)); + dstBeing->setHairColor(hairColor); + Ea::BeingNet::setSprite(dstBeing, SPRITE_WEAPON, headBottom); + Ea::BeingNet::setSprite(dstBeing, SPRITE_HEAD_BOTTOM, headMid); + Ea::BeingNet::setSprite(dstBeing, SPRITE_CLOTHES_COLOR, headTop); + Ea::BeingNet::setSprite(dstBeing, SPRITE_HAIR, shoes); + Ea::BeingNet::setSprite(dstBeing, SPRITE_SHOES, gloves); + Ea::BeingNet::setSprite(dstBeing, SPRITE_BODY, weapon, "", + ItemColor_one, true); + Ea::BeingNet::setSprite(dstBeing, SPRITE_FLOOR, shield); + } + else if (dstBeing->getType() == ActorType::Npc + && serverFeatures->haveNpcGender()) + { + setServerGender(dstBeing, gender); + } + + uint16_t srcX, srcY, dstX, dstY; + msg.readCoordinatePair(srcX, srcY, dstX, dstY, "move path"); + if (!disguiseId) + { + dstBeing->setAction(BeingAction::STAND, 0); + dstBeing->setTileCoords(srcX, srcY); + if (!serverFeatures->haveMove3()) + dstBeing->setDestination(dstX, dstY); + } + + msg.readUInt8("unknown"); + msg.readUInt8("unknown"); + msg.readUInt8("unknown"); + msg.readUInt8("unknown"); + msg.readUInt8("unknown"); + + dstBeing->setStunMode(stunMode); + dstBeing->setStatusEffectBlock(0, static_cast<uint16_t>( + (statusEffects >> 16) & 0xffff)); + dstBeing->setStatusEffectBlock(16, static_cast<uint16_t>( + statusEffects & 0xffff)); + BLOCK_END("BeingNet::processBeingVisibleOrMove") +} + +void BeingNet::processBeingSpawn(Net::MessageIn &msg) +{ + BLOCK_START("BeingNet::processBeingSpawn") + // skipping this packet + Ea::BeingNet::mSpawnId = msg.readBeingId("being id"); + msg.readInt16("speed"); + msg.readInt16("opt1"); + msg.readInt16("opt2"); + msg.readInt16("option"); + msg.readInt16("disguise"); + msg.skip(25, "unused"); + BLOCK_END("BeingNet::processBeingSpawn") +} + +void BeingNet::processSkillCasting(Net::MessageIn &msg) +{ + msg.readInt32("src id"); + msg.readInt32("dst id"); + msg.readInt16("dst x"); + msg.readInt16("dst y"); + msg.readInt16("skill num"); + msg.readInt32("skill get p1"); + msg.readInt32("cast time"); +} + +void BeingNet::processBeingStatusChange(Net::MessageIn &msg) +{ + BLOCK_START("BeingNet::processBeingStatusChange") + if (!actorManager) + { + BLOCK_END("BeingNet::processBeingStatusChange") + return; + } + + // Status change + const uint16_t status = msg.readInt16("status"); + const BeingId id = msg.readBeingId("being id"); + const Enable flag = fromBool( + msg.readUInt8("flag: 0: stop, 1: start"), Enable); + + Being *const dstBeing = actorManager->findBeing(id); + if (dstBeing) + dstBeing->setStatusEffect(status, flag); + BLOCK_END("BeingNet::processBeingStatusChange") +} + +void BeingNet::processBeingMove2(Net::MessageIn &msg) +{ + BLOCK_START("BeingNet::processBeingMove2") + if (!actorManager) + { + BLOCK_END("BeingNet::processBeingMove2") + return; + } + + /* + * A simplified movement packet, used by the + * later versions of eAthena for both mobs and + * players + */ + Being *const dstBeing = actorManager->findBeing( + msg.readBeingId("being id")); + + /* + * This packet doesn't have enough info to actually + * create a new being, so if the being isn't found, + * we'll just pretend the packet didn't happen + */ + + if (!dstBeing) + { + BLOCK_END("BeingNet::processBeingMove2") + return; + } + + uint16_t srcX, srcY, dstX, dstY; + msg.readCoordinatePair(srcX, srcY, dstX, dstY, "move path"); + msg.readInt32("tick"); + + dstBeing->setAction(BeingAction::STAND, 0); + dstBeing->setTileCoords(srcX, srcY); + dstBeing->setDestination(dstX, dstY); + if (dstBeing->getType() == ActorType::Player) + dstBeing->setMoveTime(); + BLOCK_END("BeingNet::processBeingMove2") +} + +void BeingNet::processBeingChangeDirection(Net::MessageIn &msg) +{ + BLOCK_START("BeingNet::processBeingChangeDirection") + if (!actorManager) + { + BLOCK_END("BeingNet::processBeingChangeDirection") + return; + } + + Being *const dstBeing = actorManager->findBeing( + msg.readBeingId("being id")); + + if (!dstBeing) + { + msg.readInt16("unused"); + msg.readUInt8("direction"); + BLOCK_END("BeingNet::processBeingChangeDirection"); + return; + } + + msg.readInt16("unused"); + + const uint8_t dir = Net::MessageIn::fromServerDirection( + static_cast<uint8_t>(msg.readUInt8("direction") & 0x0FU)); + dstBeing->setDirection(dir); + if (localPlayer) + localPlayer->imitateDirection(dstBeing, dir); + BLOCK_END("BeingNet::processBeingChangeDirection") +} + +void BeingNet::processPlaterStatusChange(Net::MessageIn &msg) +{ + BLOCK_START("BeingNet::processPlayerStop") + if (!actorManager) + { + BLOCK_END("BeingNet::processPlayerStop") + return; + } + + // Change in players' flags + const BeingId id = msg.readBeingId("account id"); + Being *const dstBeing = actorManager->findBeing(id); + if (!dstBeing) + return; + + const uint16_t stunMode = msg.readInt16("stun mode"); + uint32_t statusEffects = msg.readInt16("status effect"); + statusEffects |= (static_cast<uint32_t>(msg.readInt16("opt?"))) << 16; + msg.readUInt8("Unused?"); + + dstBeing->setStunMode(stunMode); + dstBeing->setStatusEffectBlock(0, static_cast<uint16_t>( + (statusEffects >> 16) & 0xffff)); + dstBeing->setStatusEffectBlock(16, static_cast<uint16_t>( + statusEffects & 0xffff)); + BLOCK_END("BeingNet::processPlayerStop") +} + +void BeingNet::processBeingResurrect(Net::MessageIn &msg) +{ + BLOCK_START("BeingNet::processBeingResurrect") + if (!actorManager || !localPlayer) + { + BLOCK_END("BeingNet::processBeingResurrect") + return; + } + + // A being changed mortality status + + const BeingId id = msg.readBeingId("being id"); + Being *const dstBeing = actorManager->findBeing(id); + if (!dstBeing) + { + BLOCK_END("BeingNet::processBeingResurrect") + return; + } + + // If this is player's current target, clear it. + if (dstBeing == localPlayer->getTarget()) + localPlayer->stopAttack(); + + if (msg.readUInt8("flag?") == 1U) + dstBeing->setAction(BeingAction::STAND, 0); + BLOCK_END("BeingNet::processBeingResurrect") +} + +void BeingNet::processPlayerGuilPartyInfo(Net::MessageIn &msg) +{ + BLOCK_START("BeingNet::processPlayerGuilPartyInfo") + if (!actorManager) + { + BLOCK_END("BeingNet::processPlayerGuilPartyInfo") + return; + } + + Being *const dstBeing = actorManager->findBeing( + msg.readBeingId("being id")); + + if (dstBeing) + { + dstBeing->setPartyName(msg.readString(24, "party name")); + if (!guildManager || !GuildManager::getEnableGuildBot()) + { + dstBeing->setGuildName(msg.readString(24, "guild name")); + dstBeing->setGuildPos(msg.readString(24, "guild pos")); + } + else + { + msg.readString(24, "guild name"); + msg.readString(24, "guild pos"); + } + dstBeing->addToCache(); + msg.readString(24, "?"); + } + else + { + msg.readString(24, "party name"); + msg.readString(24, "guild name"); + msg.readString(24, "guild pos"); + msg.readString(24, "?"); + } + BLOCK_END("BeingNet::processPlayerGuilPartyInfo") +} + +void BeingNet::processBeingSelfEffect(Net::MessageIn &msg) +{ + BLOCK_START("BeingNet::processBeingSelfEffect") + if (!effectManager || !actorManager) + { + BLOCK_END("BeingNet::processBeingSelfEffect") + return; + } + + const BeingId id = msg.readBeingId("being id"); + Being *const being = actorManager->findBeing(id); + if (!being) + { + BLOCK_END("BeingNet::processBeingSelfEffect") + return; + } + + const int effectType = msg.readInt32("effect type"); + + if (Particle::enabled) + effectManager->trigger(effectType, being); + + // +++ need dehard code effectType == 3 + if (effectType == 3 && being->getType() == ActorType::Player + && socialWindow) + { // reset received damage + socialWindow->resetDamage(being->getName()); + } + BLOCK_END("BeingNet::processBeingSelfEffect") +} + +void BeingNet::processSkillCastCancel(Net::MessageIn &msg) +{ + msg.readInt32("skill id"); +} + +void BeingNet::processIpResponse(Net::MessageIn &msg) +{ + BLOCK_START("BeingNet::processIpResponse") + if (!actorManager) + { + BLOCK_END("BeingNet::processIpResponse") + return; + } + + Being *const dstBeing = actorManager->findBeing( + msg.readBeingId("being id")); + if (dstBeing) + dstBeing->setIp(ipToString(msg.readInt32("ip address"))); + BLOCK_END("BeingNet::processIpResponse") +} + +void BeingNet::processPvpSet(Net::MessageIn &msg) +{ + BLOCK_START("BeingNet::processPvpSet") + const BeingId id = msg.readBeingId("being id"); + const int rank = msg.readInt32("rank"); + int teamId = 0; + if (serverFeatures->haveTeamId()) + teamId = msg.readInt32("team"); + else + msg.readInt32("num"); + if (actorManager) + { + Being *const dstBeing = actorManager->findBeing(id); + if (dstBeing) + { + dstBeing->setPvpRank(rank); + dstBeing->setTeamId(static_cast<uint16_t>(teamId)); + dstBeing->addToCache(); + } + } + BLOCK_END("BeingNet::processPvpSet") +} + +void BeingNet::applyPlayerAction(Net::MessageIn &msg, + Being *const being, + const uint8_t type) +{ + if (!being) + return; + switch (type) + { + case 0: + being->setAction(BeingAction::STAND, 0); + localPlayer->imitateAction(being, BeingAction::STAND); + break; + + case 1: + if (being->getCurrentAction() != BeingAction::DEAD) + { + being->setAction(BeingAction::DEAD, 0); + being->recalcSpritesOrder(); + } + break; + + case 2: + being->setAction(BeingAction::SIT, 0); + localPlayer->imitateAction(being, BeingAction::SIT); + break; + + default: + UNIMPLIMENTEDPACKET; + break; + } +} + +void BeingNet::setServerGender(Being *const being, const uint8_t gender) +{ + if (!being) + return; + switch (gender) + { + case 2: + being->setGender(Gender::FEMALE); + break; + case 3: + being->setGender(Gender::MALE); + break; + case 4: + being->setGender(Gender::OTHER); + break; + default: + being->setGender(Gender::UNSPECIFIED); + break; + } +} + +} // namespace TmwAthena diff --git a/src/net/tmwa/beingnet.h b/src/net/tmwa/beingnet.h new file mode 100644 index 000000000..268cc0723 --- /dev/null +++ b/src/net/tmwa/beingnet.h @@ -0,0 +1,66 @@ +/* + * The ManaPlus Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011-2015 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef NET_TMWA_BEING_H +#define NET_TMWA_BEING_H + +#include "net/ea/beinghandler.h" + +#include "net/tmwa/messagehandler.h" + +namespace TmwAthena +{ + namespace BeingNet + { + void processBeingChangeLook(Net::MessageIn &msg); + void processBeingChangeLook2(Net::MessageIn &msg); + void processBeingVisible(Net::MessageIn &msg); + void processBeingMove(Net::MessageIn &msg); + void processPlayerUpdate1(Net::MessageIn &msg); + void processPlayerUpdate2(Net::MessageIn &msg); + void processPlayerMove(Net::MessageIn &msg); + void processBeingSpawn(Net::MessageIn &msg); + void processSkillCasting(Net::MessageIn &msg); + void processBeingStatusChange(Net::MessageIn &msg); + void processBeingMove2(Net::MessageIn &msg); + void processBeingChangeDirection(Net::MessageIn &msg); + void processBeingChangeLookContinue(Net::MessageIn &msg, + Being *const dstBeing, + const uint8_t type, + const int id, + const int id2) A_NONNULL(2); + void processPlaterStatusChange(Net::MessageIn &msg); + void processBeingResurrect(Net::MessageIn &msg); + void processPlayerGuilPartyInfo(Net::MessageIn &msg); + void processBeingSelfEffect(Net::MessageIn &msg); + void processSkillCastCancel(Net::MessageIn &msg); + void processIpResponse(Net::MessageIn &msg); + void processPvpSet(Net::MessageIn &msg); + void applyPlayerAction(Net::MessageIn &msg, + Being *const being, + const uint8_t type); + void setServerGender(Being *const being, + const uint8_t gender); + } // namespace Being +} // namespace TmwAthena + +#endif // NET_TMWA_BEING_H |