/* * 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 . */ #include "net/ea/beinghandler.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 { 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 unsigned char 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( 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(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) { UNIMPLIMENTEDPACKET; msg.readInt16("skill id"); msg.readInt16("heal"); msg.readInt32("dst id"); msg.readInt32("src id"); msg.readUInt8("fail"); } 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") } } // namespace Ea