From 78ab801cc5dfe687718ec7e027c3050bed62a1e9 Mon Sep 17 00:00:00 2001 From: Andrei Karas Date: Thu, 19 May 2011 23:41:05 +0300 Subject: Start separating netcode to functions and moving some code to ea name space for future usage for different server types. Processed classes: adminhandler, beinghandler, buysellhandler. --- src/CMakeLists.txt | 14 +- src/Makefile.am | 7 + src/net/ea/adminhandler.cpp | 64 ++ src/net/ea/adminhandler.h | 58 ++ src/net/ea/beinghandler.cpp | 820 ++++++++++++++++++++++ src/net/ea/beinghandler.h | 111 +++ src/net/ea/buysellhandler.cpp | 180 +++++ src/net/ea/buysellhandler.h | 66 ++ src/net/ea/eaprotocol.h | 42 ++ src/net/tmwa/adminhandler.cpp | 40 +- src/net/tmwa/adminhandler.h | 16 +- src/net/tmwa/beinghandler.cpp | 1424 ++++++++++++--------------------------- src/net/tmwa/beinghandler.h | 14 +- src/net/tmwa/buysellhandler.cpp | 187 ++--- src/net/tmwa/buysellhandler.h | 17 +- 15 files changed, 1847 insertions(+), 1213 deletions(-) create mode 100644 src/net/ea/adminhandler.cpp create mode 100644 src/net/ea/adminhandler.h create mode 100644 src/net/ea/beinghandler.cpp create mode 100644 src/net/ea/beinghandler.h create mode 100644 src/net/ea/buysellhandler.cpp create mode 100644 src/net/ea/buysellhandler.h create mode 100644 src/net/ea/eaprotocol.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 825cecd01..4cbc8154c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -575,6 +575,16 @@ SET(SRCS mumblemanager.h ) +SET(SRCS_EVOL + net/ea/adminhandler.cpp + net/ea/adminhandler.h + net/ea/beinghandler.cpp + net/ea/beinghandler.h + net/ea/buysellhandler.cpp + net/ea/buysellhandler.h + net/ea/eaprotocol.h + ) + SET(SRCS_TMWA net/tmwa/gui/guildtab.cpp net/tmwa/gui/guildtab.h @@ -707,9 +717,9 @@ ENDIF () SET (PROGRAMS manaplus) IF (ENABLE_MANASERV) - ADD_EXECUTABLE(manaplus WIN32 ${SRCS} ${SRCS_MANASERV} ${SRCS_TMWA}) + ADD_EXECUTABLE(manaplus WIN32 ${SRCS} ${SRCS_MANASERV} ${SRCS_TMWA} ${SRCS_EVOL}) ELSE(ENABLE_MANASERV) - ADD_EXECUTABLE(manaplus WIN32 ${SRCS} ${SRCS_TMWA}) + ADD_EXECUTABLE(manaplus WIN32 ${SRCS} ${SRCS_TMWA} ${SRCS_EVOL}) ENDIF(ENABLE_MANASERV) TARGET_LINK_LIBRARIES(manaplus diff --git a/src/Makefile.am b/src/Makefile.am index 956595304..31674723d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -543,6 +543,13 @@ manaplus_SOURCES += enet/callbacks.c \ endif manaplus_SOURCES += \ + net/ea/adminhandler.cpp \ + net/ea/adminhandler.h \ + net/ea/beinghandler.cpp \ + net/ea/beinghandler.h \ + net/ea/buysellhandler.cpp \ + net/ea/buysellhandler.h \ + net/ea/eaprotocol.h \ net/tmwa/gui/guildtab.cpp \ net/tmwa/gui/guildtab.h \ net/tmwa/gui/partytab.cpp \ diff --git a/src/net/ea/adminhandler.cpp b/src/net/ea/adminhandler.cpp new file mode 100644 index 000000000..9dd525ae9 --- /dev/null +++ b/src/net/ea/adminhandler.cpp @@ -0,0 +1,64 @@ +/* + * The ManaPlus Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011 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/adminhandler.h" + +#include "net/chathandler.h" +#include "net/net.h" + +#include + +namespace Ea +{ + +void AdminHandler::kick(const std::string &name) +{ + Net::getChatHandler()->talk("@kick " + name); +} + +void AdminHandler::ban(int playerId _UNUSED_) +{ + // Not supported +} + +void AdminHandler::ban(const std::string &name) +{ + Net::getChatHandler()->talk("@ban " + name); +} + +void AdminHandler::unban(int playerId _UNUSED_) +{ + // Not supported +} + +void AdminHandler::unban(const std::string &name) +{ + Net::getChatHandler()->talk("@unban " + name); +} + +void AdminHandler::mute(int playerId _UNUSED_, int type _UNUSED_, + int limit _UNUSED_) +{ + return; // Still looking into this +} + +} // namespace Ea diff --git a/src/net/ea/adminhandler.h b/src/net/ea/adminhandler.h new file mode 100644 index 000000000..67b5cdc11 --- /dev/null +++ b/src/net/ea/adminhandler.h @@ -0,0 +1,58 @@ +/* + * The ManaPlus Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011 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 . + */ + +#ifndef NET_EA_ADMINHANDLER_H +#define NET_EA_ADMINHANDLER_H + +#include "net/adminhandler.h" +#include "net/net.h" + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +namespace Ea +{ + +class AdminHandler : public Net::AdminHandler +{ + public: + virtual ~AdminHandler() { } + + virtual void kick(const std::string &name); + + virtual void ban(int playerId); + + virtual void ban(const std::string &name); + + virtual void unban(int playerId); + + virtual void unban(const std::string &name); + + virtual void mute(int playerId, int type, int limit); +}; + +} // namespace Ea + +#endif // NET_EA_ADMINHANDLER_H diff --git a/src/net/ea/beinghandler.cpp b/src/net/ea/beinghandler.cpp new file mode 100644 index 000000000..91c83a4e9 --- /dev/null +++ b/src/net/ea/beinghandler.cpp @@ -0,0 +1,820 @@ +/* + * The ManaPlus Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011 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 "net/ea/eaprotocol.h" + +#include "actorspritemanager.h" +#include "being.h" +#include "client.h" +#include "effectmanager.h" +#include "guild.h" +#include "keyboardconfig.h" +#include "localplayer.h" +#include "log.h" +#include "party.h" +#include "playerrelations.h" +#include "configuration.h" + +#include "gui/botcheckerwindow.h" +#include "gui/outfitwindow.h" +#include "gui/socialwindow.h" +#include "gui/killstats.h" + +#include "utils/gettext.h" +#include "utils/stringutils.h" + +#include "net/playerhandler.h" +#include "net/net.h" + +#include "resources/colordb.h" + +#include + +namespace Ea +{ +const int EMOTION_TIME = 500; /**< Duration of emotion icon */ + +BeingHandler::BeingHandler(bool enableSync): + mSync(enableSync), + mSpawnId(0) +{ +} + +Being *BeingHandler::createBeing(int id, short job) +{ + if (!actorSpriteManager) + return 0; + + ActorSprite::Type type = ActorSprite::UNKNOWN; + if (job <= 25 || (job >= 4001 && job <= 4049)) + type = ActorSprite::PLAYER; + else if (job >= 46 && job <= 1000) + type = ActorSprite::NPC; + else if (job > 1000 && job <= 2000) + type = ActorSprite::MONSTER; + else if (job == 45) + type = ActorSprite::PORTAL; + + Being *being = actorSpriteManager->createBeing(id, type, job); + + if (type == ActorSprite::PLAYER || type == ActorSprite::NPC) + { + if (!being->updateFromCache()) + { + requestNameById(id); + } + else + { + if (player_node) + player_node->checkNewName(being); + } + } + if (type == Being::PLAYER) + { + if (botCheckerWindow) + botCheckerWindow->updateList(); + if (socialWindow) + socialWindow->updateActiveList(); + } + return being; +} + +void BeingHandler::setSprite(Being *being, unsigned int slot, int id, + std::string color, unsigned char colorId, + bool isWeapon, bool isTempSprite) +{ + if (!being) + return; + being->setSprite(slot, id, color, colorId, isWeapon, isTempSprite); +} + +void BeingHandler::processBeingVisibleOrMove(Net::MessageIn &msg, bool visible) +{ + if (!actorSpriteManager) + return; + + int id; + short job, speed, gender; + Uint16 headTop, headMid, headBottom; + Uint16 shoes, gloves; + Uint16 weapon, shield; + Uint16 stunMode; + Uint32 statusEffects; + int guild; + Being *dstBeing; + int hairStyle, hairColor; + int hp, maxHP, oldHP; + int spawnId; + + // Information about a being in range + id = msg.readInt32(); + if (id == mSpawnId) + spawnId = mSpawnId; + else + spawnId = 0; + mSpawnId = 0; + speed = msg.readInt16(); + stunMode = msg.readInt16(); // opt1 + statusEffects = msg.readInt16(); // opt2 + statusEffects |= (static_cast( + msg.readInt16())) << 16; // option + job = msg.readInt16(); // class + + dstBeing = actorSpriteManager->findBeing(id); + + if (dstBeing && dstBeing->getType() == Being::MONSTER + && !dstBeing->isAlive()) + { + actorSpriteManager->destroy(dstBeing); + actorSpriteManager->erase(dstBeing); + dstBeing = 0; + } + + if (!dstBeing) + { + // Being with id >= 110000000 and job 0 are better + // known as ghosts, so don't create those. + if (job == 0 && id >= 110000000) + return; + + if (actorSpriteManager->isBlocked(id) == true) + return; + + dstBeing = createBeing(id, job); + + if (!dstBeing) + return; + + if (job == 1022 && killStats) + killStats->jackoAlive(dstBeing->getId()); + } + else + { + // undeleting marked for deletion being + if (dstBeing->getType() == Being::NPC) + actorSpriteManager->undelete(dstBeing); + } + + if (dstBeing->getType() == Being::PLAYER) + dstBeing->setMoveTime(); + + if (spawnId) + { + dstBeing->setAction(Being::SPAWN); + } + else if (visible) + { + dstBeing->clearPath(); + dstBeing->setActionTime(tick_time); + dstBeing->setAction(Being::STAND); + } + + // Prevent division by 0 when calculating frame + if (speed == 0) + speed = 150; + + dstBeing->setWalkSpeed(Vector(speed, speed, 0)); + dstBeing->setSubtype(job); + if (dstBeing->getType() == ActorSprite::MONSTER && player_node) + player_node->checkNewName(dstBeing); + + hairStyle = msg.readInt16(); + weapon = msg.readInt16(); + headBottom = msg.readInt16(); + + if (!visible) + msg.readInt32(); // server tick + + shield = msg.readInt16(); + headTop = msg.readInt16(); + headMid = msg.readInt16(); + hairColor = msg.readInt16(); + shoes = msg.readInt16(); // clothes color - "abused" as shoes + + if (dstBeing->getType() == ActorSprite::MONSTER) + { + hp = msg.readInt32(); + maxHP = msg.readInt32(); + if (hp && maxHP) + { + oldHP = dstBeing->getHP(); + if (!oldHP || oldHP > hp) + dstBeing->setHP(hp); + dstBeing->setMaxHP(maxHP); + } + gloves = 0; + guild = 0; + } + else + { + gloves = msg.readInt16(); // head dir - "abused" as gloves + guild = msg.readInt32(); // guild + msg.readInt16(); // guild emblem + } +// logger->log("being guild: " + toString(guild)); +/* + if (guild == 0) + dstBeing->clearGuilds(); + else + dstBeing->setGuild(Guild::getGuild(static_cast(guild))); +*/ + + msg.readInt16(); // manner + dstBeing->setStatusEffectBlock(32, msg.readInt16()); // opt3 + msg.readInt8(); // karma + gender = msg.readInt8(); + + if (dstBeing->getType() == ActorSprite::PLAYER) + { + dstBeing->setGender((gender == 0) + ? GENDER_FEMALE : GENDER_MALE); + // Set these after the gender, as the sprites may be gender-specific + setSprite(dstBeing, EA_SPRITE_HAIR, hairStyle * -1, + ColorDB::getHairColor(hairColor)); + setSprite(dstBeing, EA_SPRITE_BOTTOMCLOTHES, headBottom); + setSprite(dstBeing, EA_SPRITE_TOPCLOTHES, headMid); + setSprite(dstBeing, EA_SPRITE_HAT, headTop); + setSprite(dstBeing, EA_SPRITE_SHOE, shoes); + setSprite(dstBeing, EA_SPRITE_GLOVES, gloves); + setSprite(dstBeing, EA_SPRITE_WEAPON, weapon, "", true); + if (!config.getBoolValue("hideShield")) + setSprite(dstBeing, EA_SPRITE_SHIELD, shield); + } + else if (dstBeing->getType() == ActorSprite::NPC) + { + switch (gender) + { + case 2: + dstBeing->setGender(GENDER_FEMALE); + break; + case 3: + dstBeing->setGender(GENDER_MALE); + break; + default: + dstBeing->setGender(GENDER_UNSPECIFIED); + break; + } + } + + + if (!visible) + { + Uint16 srcX, srcY, dstX, dstY; + msg.readCoordinatePair(srcX, srcY, dstX, dstY); + dstBeing->setAction(Being::STAND); + dstBeing->setTileCoords(srcX, srcY); + dstBeing->setDestination(dstX, dstY); +// if (player_node && player_node->getTarget() == dstBeing) +// player_node->targetMoved(); + } + else + { + Uint8 dir; + Uint16 x, y; + msg.readCoordinates(x, y, dir); + dstBeing->setTileCoords(x, y); + + + if (job == 45 && socialWindow && outfitWindow) + { + int num = socialWindow->getPortalIndex(x, y); + if (num >= 0) + { + dstBeing->setName(keyboard.getKeyShortString( + outfitWindow->keyName(num))); + } + else + { + dstBeing->setName(""); + } + } + + dstBeing->setDirection(dir); + } + + msg.readInt8(); // unknown + msg.readInt8(); // unknown +// msg.readInt8(); // unknown / sit + msg.readInt16(); + + dstBeing->setStunMode(stunMode); + dstBeing->setStatusEffectBlock(0, static_cast( + (statusEffects >> 16) & 0xffff)); + dstBeing->setStatusEffectBlock(16, statusEffects & 0xffff); + +} + +void BeingHandler::processBeingMove2(Net::MessageIn &msg) +{ + if (!actorSpriteManager) + return; + + /* + * A simplified movement packet, used by the + * later versions of eAthena for both mobs and + * players + */ + Being *dstBeing = actorSpriteManager->findBeing(msg.readInt32()); + + /* + * 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) + return; + + Uint16 srcX, srcY, dstX, dstY; + msg.readCoordinatePair(srcX, srcY, dstX, dstY); + msg.readInt32(); // Server tick + + dstBeing->setAction(Being::STAND); + dstBeing->setTileCoords(srcX, srcY); + dstBeing->setDestination(dstX, dstY); + if (dstBeing->getType() == Being::PLAYER) + dstBeing->setMoveTime(); +} + +void BeingHandler::processBeingSpawn(Net::MessageIn &msg) +{ + // skipping this packet + mSpawnId = msg.readInt32(); // id + msg.readInt16(); // speed + msg.readInt16(); // opt1 + msg.readInt16(); // opt2 + msg.readInt16(); // option + msg.readInt16(); // disguise +} + +void BeingHandler::processBeingRemove(Net::MessageIn &msg) +{ + if (!actorSpriteManager || !player_node) + return; + + // A being should be removed or has died + + int id = msg.readInt32(); + Being *dstBeing = actorSpriteManager->findBeing(id); + if (!dstBeing) + return; + + player_node->followMoveTo(dstBeing, player_node->getNextDestX(), + player_node->getNextDestY()); + + // If this is player's current target, clear it. + if (dstBeing == player_node->getTarget()) + player_node->stopAttack(); + + if (msg.readInt8() == 1) + { + dstBeing->setAction(Being::DEAD); + if (dstBeing->getName() == "Jack O" && killStats) + killStats->jackoDead(id); + } + else + { + if (dstBeing->getType() == Being::PLAYER) + { + if (botCheckerWindow) + botCheckerWindow->updateList(); + if (socialWindow) + socialWindow->updateActiveList(); + } + actorSpriteManager->destroy(dstBeing); + } +} + +void BeingHandler::processBeingResurrect(Net::MessageIn &msg) +{ + if (!actorSpriteManager || !player_node) + return; + + // A being changed mortality status + + int id = msg.readInt32(); + Being *dstBeing = actorSpriteManager->findBeing(id); + if (!dstBeing) + return; + + // If this is player's current target, clear it. + if (dstBeing == player_node->getTarget()) + player_node->stopAttack(); + + if (msg.readInt8() == 1) + dstBeing->setAction(Being::STAND); +} + + +void BeingHandler::processSkillDamage(Net::MessageIn &msg) +{ + if (!actorSpriteManager) + return; + + Being *srcBeing; + Being *dstBeing; + int param1; + + msg.readInt16(); // Skill Id + srcBeing = actorSpriteManager->findBeing(msg.readInt32()); + dstBeing = actorSpriteManager->findBeing(msg.readInt32()); + msg.readInt32(); // Server tick + msg.readInt32(); // src speed + msg.readInt32(); // dst speed + param1 = msg.readInt32(); // Damage + msg.readInt16(); // Skill level + msg.readInt16(); // Div + msg.readInt8(); // Skill hit/type (?) + if (dstBeing) + { + // Perhaps a new skill attack type should be created and used? +// if (dstSpeed) +// dstBeing->setAttackDelay(dstSpeed); + dstBeing->takeDamage(srcBeing, param1, Being::HIT); + } + if (srcBeing) + { +// if (srcSpeed) +// srcBeing->setAttackDelay(srcSpeed); + srcBeing->handleAttack(dstBeing, param1, Being::HIT); + } +} + +void BeingHandler::processBeingAction(Net::MessageIn &msg) +{ + if (!actorSpriteManager) + return; + + Being *srcBeing; + Being *dstBeing; + int param1; + int type; + + srcBeing = actorSpriteManager->findBeing(msg.readInt32()); + dstBeing = actorSpriteManager->findBeing(msg.readInt32()); + + msg.readInt32(); // server tick + int srcSpeed = msg.readInt32(); // src speed + msg.readInt32(); // dst speed + param1 = msg.readInt16(); + msg.readInt16(); // param 2 + type = msg.readInt8(); + msg.readInt16(); // param 3 + + switch (type) + { + case Being::HIT: // Damage + case Being::CRITICAL: // Critical Damage + case Being::MULTI: // Critical Damage + case Being::REFLECT: // Reflected Damage + case Being::FLEE: // Lucky Dodge + if (dstBeing) + { +// if (dstSpeed) +// dstBeing->setAttackDelay(dstSpeed); + dstBeing->takeDamage(srcBeing, param1, + static_cast(type)); + } + if (srcBeing) + { + if (srcSpeed && srcBeing->getType() == Being::PLAYER) + srcBeing->setAttackDelay(srcSpeed); + srcBeing->handleAttack(dstBeing, param1, + static_cast(type)); + if (srcBeing->getType() == Being::PLAYER) + srcBeing->setAttackTime(); + } + break; + + case 0x02: // Sit + if (srcBeing) + { + srcBeing->setAction(Being::SIT); + if (srcBeing->getType() == Being::PLAYER) + { + srcBeing->setMoveTime(); + if (player_node) + { + player_node->imitateAction( + srcBeing, Being::SIT); + } + } + } + break; + + case 0x03: // Stand up + if (srcBeing) + { + srcBeing->setAction(Being::STAND); + if (srcBeing->getType() == Being::PLAYER) + { + srcBeing->setMoveTime(); + if (player_node) + { + player_node->imitateAction( + srcBeing, Being::STAND); + } + } + } + break; + default: + break; +/* + logger->log("QQQ1 SMSG_BEING_ACTION:"); + if (srcBeing) + logger->log("srcBeing:" + toString(srcBeing->getId())); + if (dstBeing) + logger->log("dstBeing:" + toString(dstBeing->getId())); + logger->log("type: " + toString(type)); +*/ + } +} + +void BeingHandler::processBeingSelfEffect(Net::MessageIn &msg) +{ + if (!effectManager || !actorSpriteManager) + return; + + int id; + + id = static_cast(msg.readInt32()); + Being* being = actorSpriteManager->findBeing(id); + if (!being) + return; + + int effectType = msg.readInt32(); + + effectManager->trigger(effectType, being); + + //+++ need dehard code effectType == 3 + if (being && effectType == 3 + && being->getType() == Being::PLAYER + && socialWindow) + { //reset received damage + socialWindow->resetDamage(being->getName()); + } +} + +void BeingHandler::processBeingEmotion(Net::MessageIn &msg) +{ + if (!player_node || !actorSpriteManager) + return; + + Being *dstBeing; + + if (!(dstBeing = actorSpriteManager->findBeing(msg.readInt32()))) + return; + + if (player_relations.hasPermission(dstBeing, + PlayerRelation::EMOTE)) + { + unsigned char emote = msg.readInt8(); + if (emote) + { + dstBeing->setEmote(emote, EMOTION_TIME); + player_node->imitateEmote(dstBeing, emote); + } + } + if (dstBeing->getType() == Being::PLAYER) + dstBeing->setOtherTime(); +} + +void BeingHandler::processNameResponse(Net::MessageIn &msg) +{ + if (!player_node || !actorSpriteManager) + return; + + Being *dstBeing; + + int beingId = msg.readInt32(); + if ((dstBeing = actorSpriteManager->findBeing(beingId))) + { + if (beingId == player_node->getId()) + { + player_node->pingResponse(); + } + else + { + dstBeing->setName(msg.readString(24)); + dstBeing->updateGuild(); + dstBeing->addToCache(); + + if (dstBeing->getType() == Being::PLAYER) + dstBeing->updateColors(); + + if (player_node) + { + Party *party = player_node->getParty(); + if (party && party->isMember(dstBeing->getId())) + { + PartyMember *member = party->getMember( + dstBeing->getId()); + + if (member) + member->setName(dstBeing->getName()); + } + player_node->checkNewName(dstBeing); + } + } + } +} + +void BeingHandler::processIpResponse(Net::MessageIn &msg) +{ + if (!actorSpriteManager) + return; + + Being *dstBeing; + + if ((dstBeing = actorSpriteManager->findBeing( + msg.readInt32()))) + { + dstBeing->setIp(ipToString(msg.readInt32())); + } +} + +void BeingHandler::processPlayerGuilPartyInfo(Net::MessageIn &msg) +{ + if (!actorSpriteManager) + return; + + Being *dstBeing; + + if ((dstBeing = actorSpriteManager->findBeing(msg.readInt32()))) + { + dstBeing->setPartyName(msg.readString(24)); + dstBeing->setGuildName(msg.readString(24)); + dstBeing->setGuildPos(msg.readString(24)); + dstBeing->addToCache(); + msg.readString(24); // Discard this + } +} + +void BeingHandler::processBeingChangeDirection(Net::MessageIn &msg) +{ + if (!actorSpriteManager) + return; + + Being *dstBeing; + + if (!(dstBeing = actorSpriteManager->findBeing(msg.readInt32()))) + return; + + msg.readInt16(); // unused + + unsigned char dir = msg.readInt8(); + dstBeing->setDirection(dir); + if (player_node) + player_node->imitateDirection(dstBeing, dir); +} + +void BeingHandler::processPlayerStop(Net::MessageIn &msg) +{ + if (!actorSpriteManager || !player_node) + return; + + /* + * Instruction from server to stop walking at x, y. + * + * Some people like having this enabled. Others absolutely + * despise it. So I'm setting to so that it only affects the + * local player if the person has set a key "EnableSync" to "1" + * in their config.xml file. + * + * This packet will be honored for all other beings, regardless + * of the config setting. + */ + + int id = msg.readInt32(); + + if (mSync || id != player_node->getId()) + { + Being *dstBeing = actorSpriteManager->findBeing(id); + if (dstBeing) + { + Uint16 x, y; + x = msg.readInt16(); + y = msg.readInt16(); + dstBeing->setTileCoords(x, y); + if (dstBeing->getCurrentAction() == Being::MOVE) + dstBeing->setAction(Being::STAND); + } + } +} + +void BeingHandler::processPlayerMoveToAttack(Net::MessageIn &msg _UNUSED_) +{ + /* + * This is an *advisory* message, telling the client that + * it needs to move the character before attacking + * a target (out of range, obstruction in line of fire). + * We can safely ignore this... + */ + if (player_node) + player_node->fixAttackTarget(); +} + +void BeingHandler::processPlaterStatusChange(Net::MessageIn &msg) +{ + if (!actorSpriteManager) + return; + + // Change in players' flags + + int id = msg.readInt32(); + Being *dstBeing = actorSpriteManager->findBeing(id); + if (!dstBeing) + return; + + Uint16 stunMode = msg.readInt16(); + Uint32 statusEffects = msg.readInt16(); + statusEffects |= (static_cast(msg.readInt16())) << 16; + msg.readInt8(); // Unused? + + dstBeing->setStunMode(stunMode); + dstBeing->setStatusEffectBlock(0, static_cast( + (statusEffects >> 16) & 0xffff)); + dstBeing->setStatusEffectBlock(16, statusEffects & 0xffff); +} + +void BeingHandler::processBeingStatusChange(Net::MessageIn &msg) +{ + if (!actorSpriteManager) + return; + + // Status change + Uint16 status = msg.readInt16(); + int id = msg.readInt32(); + int flag = msg.readInt8(); // 0: stop, 1: start + + Being *dstBeing = actorSpriteManager->findBeing(id); + if (dstBeing) + dstBeing->setStatusEffect(status, flag); +} + +void BeingHandler::processSkilCasting(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 pl + msg.readInt32(); // cast time +} + +void BeingHandler::processSkillNoDamage(Net::MessageIn &msg) +{ + msg.readInt16(); // skill id + msg.readInt16(); // heal + msg.readInt32(); // dst id + msg.readInt32(); // src id + msg.readInt8(); // fail +} + +void BeingHandler::processPvpMapMode(Net::MessageIn &msg) +{ + Game *game = Game::instance(); + if (!game) + return; + + Map *map = game->getCurrentMap(); + if (map) + map->setPvpMode(msg.readInt16()); +} + +void BeingHandler::processPvpSet(Net::MessageIn &msg) +{ + int id = msg.readInt32(); // id + int rank = msg.readInt32(); // rank + msg.readInt32(); // num + if (actorSpriteManager) + { + Being *dstBeing = actorSpriteManager->findBeing(id); + if (dstBeing) + dstBeing->setPvpRank(rank); + } +} + +} // namespace Ea diff --git a/src/net/ea/beinghandler.h b/src/net/ea/beinghandler.h new file mode 100644 index 000000000..e76cf7840 --- /dev/null +++ b/src/net/ea/beinghandler.h @@ -0,0 +1,111 @@ +/* + * The ManaPlus Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011 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 . + */ + +#ifndef NET_EA_BEINGHANDLER_H +#define NET_EA_BEINGHANDLER_H + +#include "net/beinghandler.h" +#include "net/net.h" + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +namespace Ea +{ + +class BeingHandler : public Net::BeingHandler +{ + public: + BeingHandler(bool enableSync); + + protected: + virtual void requestNameById(int id) = 0; + + virtual Being *createBeing(int id, short job); + + virtual void setSprite(Being *being, unsigned int slot, int id, + std::string color = "", + unsigned char colorId = 1, + bool isWeapon = false, + bool isTempSprite = false); + + virtual void processBeingVisibleOrMove(Net::MessageIn &msg, + bool visible); + + virtual void processBeingMove2(Net::MessageIn &msg); + + virtual void processBeingSpawn(Net::MessageIn &msg); + + virtual void processBeingRemove(Net::MessageIn &msg); + + virtual void processBeingResurrect(Net::MessageIn &msg); + + virtual void processSkillDamage(Net::MessageIn &msg); + + virtual void processBeingAction(Net::MessageIn &msg); + + virtual void processBeingSelfEffect(Net::MessageIn &msg); + + virtual void processBeingEmotion(Net::MessageIn &msg); + + virtual void processBeingChangeLook(Net::MessageIn &msg, + bool look2) = 0; + + virtual void processNameResponse(Net::MessageIn &msg); + + virtual void processIpResponse(Net::MessageIn &msg); + + virtual void processPlayerGuilPartyInfo(Net::MessageIn &msg); + + virtual void processBeingChangeDirection(Net::MessageIn &msg); + + virtual void processPlayerMoveUpdate(Net::MessageIn &msg, + int type) = 0; + + virtual void processPlayerStop(Net::MessageIn &msg); + + virtual void processPlayerMoveToAttack(Net::MessageIn &msg); + + virtual void processPlaterStatusChange(Net::MessageIn &msg); + + virtual void processBeingStatusChange(Net::MessageIn &msg); + + virtual void processSkilCasting(Net::MessageIn &msg); + + virtual void processSkillNoDamage(Net::MessageIn &msg); + + virtual void processPvpMapMode(Net::MessageIn &msg); + + virtual void processPvpSet(Net::MessageIn &msg); + + protected: + // Should we honor server "Stop Walking" packets + bool mSync; + int mSpawnId; +}; + +} // namespace Ea + +#endif // NET_EA_BEINGHANDLER_H diff --git a/src/net/ea/buysellhandler.cpp b/src/net/ea/buysellhandler.cpp new file mode 100644 index 000000000..35180c10f --- /dev/null +++ b/src/net/ea/buysellhandler.cpp @@ -0,0 +1,180 @@ +/* + * The ManaPlus Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011 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/buysellhandler.h" + +#include "actorspritemanager.h" +#include "configuration.h" +#include "event.h" +#include "inventory.h" +#include "item.h" +#include "localplayer.h" +#include "playerinfo.h" +#include "shopitem.h" + +#include "gui/buydialog.h" +#include "gui/buyselldialog.h" +#include "gui/selldialog.h" +#include "gui/shopwindow.h" + +#include "gui/widgets/chattab.h" + +#include "net/chathandler.h" +#include "net/messagein.h" +#include "net/net.h" + +#include "utils/gettext.h" + +namespace Ea +{ + +BuySellHandler::BuySellHandler() +{ + mNpcId = 0; +} + +void BuySellHandler::requestSellList(std::string nick) +{ + if (nick.empty() != 0 || !shopWindow) + return; + + std::string data = "!selllist " + toString(tick_time); + shopWindow->setAcceptPlayer(nick); + + if (config.getBoolValue("hideShopMessages")) + { + Net::getChatHandler()->privateMessage(nick, data); + } + else + { + if (chatWindow) + chatWindow->whisper(nick, data, BY_PLAYER); + } +} + +void BuySellHandler::requestBuyList(std::string nick) +{ + if (nick.empty() || !shopWindow) + return; + + std::string data = "!buylist " + toString(tick_time); + shopWindow->setAcceptPlayer(nick); + + if (config.getBoolValue("hideShopMessages")) + { + Net::getChatHandler()->privateMessage(nick, data); + } + else + { + if (chatWindow) + chatWindow->whisper(nick, data, BY_PLAYER); + } +} + +void BuySellHandler::sendBuyRequest(std::string nick, ShopItem* item, + int amount) +{ + if (!chatWindow || nick.empty() || !item || + amount < 1 || amount > item->getQuantity()) + { + return; + } + std::string data = strprintf("!buyitem %d %d %d", + item->getId(), item->getPrice(), amount); + + if (config.getBoolValue("hideShopMessages")) + Net::getChatHandler()->privateMessage(nick, data); + else + chatWindow->whisper(nick, data, BY_PLAYER); +} + +void BuySellHandler::sendSellRequest(std::string nick, ShopItem* item, + int amount) +{ + if (!chatWindow || nick.empty() || !item || + amount < 1 || amount > item->getQuantity()) + { + return; + } + + std::string data = strprintf("!sellitem %d %d %d", + item->getId(), item->getPrice(), amount); + + if (config.getBoolValue("hideShopMessages")) + Net::getChatHandler()->privateMessage(nick, data); + else + chatWindow->whisper(nick, data, BY_PLAYER); +} + +void BuySellHandler::processNpcBuySellChoice(Net::MessageIn &msg) +{ + if (!BuySellDialog::isActive()) + { + mNpcId = msg.readInt32(); + new BuySellDialog(mNpcId); + } +} + +void BuySellHandler::processNpcSell(Net::MessageIn &msg, int offset) +{ + msg.readInt16(); // length + int n_items = (msg.getLength() - 4) / 10; + if (n_items > 0) + { + SellDialog *dialog = new SellDialog(mNpcId); + dialog->setMoney(PlayerInfo::getAttribute(MONEY)); + + for (int k = 0; k < n_items; k++) + { + int index = msg.readInt16() - offset; + int value = msg.readInt32(); + msg.readInt32(); // OCvalue + + Item *item = PlayerInfo::getInventory()->getItem(index); + + if (item && !(item->isEquipped())) + dialog->addItem(item, value); + } + } + else + { + SERVER_NOTICE(_("Nothing to sell.")) + } +} + +void BuySellHandler::processNpcBuyResponse(Net::MessageIn &msg) +{ + if (msg.readInt8() == 0) + { + SERVER_NOTICE(_("Thanks for buying.")) + } + else + { + // Reset player money since buy dialog already assumed purchase + // would go fine + if (mBuyDialog) + mBuyDialog->setMoney(PlayerInfo::getAttribute(MONEY)); + SERVER_NOTICE(_("Unable to buy.")) + } +} + +} // namespace Ea diff --git a/src/net/ea/buysellhandler.h b/src/net/ea/buysellhandler.h new file mode 100644 index 000000000..60931ef9b --- /dev/null +++ b/src/net/ea/buysellhandler.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 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 . + */ + +#ifndef NET_EA_BUYSELLHANDLER_H +#define NET_EA_BUYSELLHANDLER_H + +#include "net/buysellhandler.h" + +#include "being.h" + +#include "net/net.h" + +class BuyDialog; + +namespace Ea +{ + +class BuySellHandler : public Net::BuySellHandler +{ + public: + BuySellHandler(); + + virtual void requestSellList(std::string nick); + virtual void requestBuyList(std::string nick); + virtual void sendBuyRequest(std::string nick, ShopItem* item, + int amount); + virtual void sendSellRequest(std::string nick, ShopItem* item, + int amount); + + virtual void processNpcBuySellChoice(Net::MessageIn &msg); + + virtual void processNpcBuy(Net::MessageIn &msg) = 0; + + virtual void processNpcSell(Net::MessageIn &msg, int offset); + + virtual void processNpcBuyResponse(Net::MessageIn &msg); + + virtual void processNpcSellResponse(Net::MessageIn &msg) = 0; + + protected: + int mNpcId; + BuyDialog *mBuyDialog; +}; + +} // namespace Ea + +#endif // NET_EA_BUYSELLHANDLER_H diff --git a/src/net/ea/eaprotocol.h b/src/net/ea/eaprotocol.h new file mode 100644 index 000000000..d72ca3515 --- /dev/null +++ b/src/net/ea/eaprotocol.h @@ -0,0 +1,42 @@ +/* + * The ManaPlus Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011 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 . + */ + +#ifndef EA_EA_PROTOCOL_H +#define EA_EA_PROTOCOL_H + +enum +{ + EA_SPRITE_BASE = 0, + EA_SPRITE_SHOE, + EA_SPRITE_BOTTOMCLOTHES, + EA_SPRITE_TOPCLOTHES, + EA_SPRITE_MISC1, + EA_SPRITE_MISC2, + EA_SPRITE_HAIR, + EA_SPRITE_HAT, + EA_SPRITE_CAPE, + EA_SPRITE_GLOVES, + EA_SPRITE_WEAPON, + EA_SPRITE_SHIELD +}; + +#endif diff --git a/src/net/tmwa/adminhandler.cpp b/src/net/tmwa/adminhandler.cpp index f33bf295b..45a285d02 100644 --- a/src/net/tmwa/adminhandler.cpp +++ b/src/net/tmwa/adminhandler.cpp @@ -22,10 +22,13 @@ #include "net/tmwa/adminhandler.h" +#include "net/ea/adminhandler.h" + #include "actorspritemanager.h" #include "being.h" #include "event.h" #include "game.h" +#include "log.h" #include "playerrelations.h" #include "net/chathandler.h" @@ -96,41 +99,4 @@ void AdminHandler::kick(int playerId) outMsg.writeInt32(playerId); } -void AdminHandler::kick(const std::string &name) -{ - Net::getChatHandler()->talk("@kick " + name); -} - -void AdminHandler::ban(int playerId _UNUSED_) -{ - // Not supported -} - -void AdminHandler::ban(const std::string &name) -{ - Net::getChatHandler()->talk("@ban " + name); -} - -void AdminHandler::unban(int playerId _UNUSED_) -{ - // Not supported -} - -void AdminHandler::unban(const std::string &name) -{ - Net::getChatHandler()->talk("@unban " + name); -} - -void AdminHandler::mute(int playerId _UNUSED_, int type _UNUSED_, - int limit _UNUSED_) -{ - return; // Still looking into this -/* - MessageOut outMsg(CMSG_ADMIN_MUTE); - outMsg.writeInt32(playerId); - outMsg.writeInt8(type); - outMsg.writeInt16(limit); -*/ -} - } // namespace TmwAthena diff --git a/src/net/tmwa/adminhandler.h b/src/net/tmwa/adminhandler.h index 60934be77..6c60398fc 100644 --- a/src/net/tmwa/adminhandler.h +++ b/src/net/tmwa/adminhandler.h @@ -26,6 +26,8 @@ #include "net/adminhandler.h" #include "net/net.h" +#include "net/ea/adminhandler.h" + #include "net/tmwa/messagehandler.h" #ifdef __GNUC__ @@ -37,7 +39,7 @@ namespace TmwAthena { -class AdminHandler : public MessageHandler, public Net::AdminHandler +class AdminHandler : public MessageHandler, public Ea::AdminHandler { public: AdminHandler(); @@ -51,18 +53,6 @@ class AdminHandler : public MessageHandler, public Net::AdminHandler void hide(bool hide); void kick(int playerId); - - void kick(const std::string &name); - - void ban(int playerId); - - void ban(const std::string &name); - - void unban(int playerId); - - void unban(const std::string &name); - - void mute(int playerId, int type, int limit); }; } // namespace TmwAthena diff --git a/src/net/tmwa/beinghandler.cpp b/src/net/tmwa/beinghandler.cpp index 582f755f1..ff458b31b 100644 --- a/src/net/tmwa/beinghandler.cpp +++ b/src/net/tmwa/beinghandler.cpp @@ -56,13 +56,8 @@ extern Net::BeingHandler *beingHandler; namespace TmwAthena { -const int EMOTION_TIME = 500; /**< Duration of emotion icon */ - -Being *createBeing(int id, short job); - BeingHandler::BeingHandler(bool enableSync): - mSync(enableSync), - mSpawnId(0) + Ea::BeingHandler(enableSync) { static const Uint16 _messages[] = { @@ -102,46 +97,6 @@ BeingHandler::BeingHandler(bool enableSync): beingHandler = this; } -Being *createBeing(int id, short job) -{ - if (!actorSpriteManager) - return 0; - - ActorSprite::Type type = ActorSprite::UNKNOWN; - if (job <= 25 || (job >= 4001 && job <= 4049)) - type = ActorSprite::PLAYER; - else if (job >= 46 && job <= 1000) - type = ActorSprite::NPC; - else if (job > 1000 && job <= 2000) - type = ActorSprite::MONSTER; - else if (job == 45) - type = ActorSprite::PORTAL; - - Being *being = actorSpriteManager->createBeing(id, type, job); - - if (type == ActorSprite::PLAYER || type == ActorSprite::NPC) - { - if (!being->updateFromCache()) - { - MessageOut outMsg(0x0094); - outMsg.writeInt32(id); //readLong(2)); - } - else - { - if (player_node) - player_node->checkNewName(being); - } - } - if (type == Being::PLAYER) - { - if (botCheckerWindow) - botCheckerWindow->updateList(); - if (socialWindow) - socialWindow->updateActiveList(); - } - return being; -} - void BeingHandler::requestNameById(int id) { MessageOut outMsg(0x0094); @@ -150,1071 +105,546 @@ void BeingHandler::requestNameById(int id) void BeingHandler::handleMessage(Net::MessageIn &msg) { - if (!actorSpriteManager) - return; - - int id; - short job, speed, gender; - Uint16 headTop, headMid, headBottom; - Uint16 shoes, gloves; - Uint16 weapon, shield; - Uint16 gmstatus; - int param1; - Uint16 stunMode; - int level; - Uint32 statusEffects; - int type, guild; - Uint16 status; - Being *srcBeing, *dstBeing; - int hairStyle, hairColor, flag; - int hp, maxHP, oldHP; - unsigned char colors[9]; - Uint8 dir; - int spawnId; - switch (msg.getId()) { case SMSG_BEING_VISIBLE: case SMSG_BEING_MOVE: - // Information about a being in range - id = msg.readInt32(); - if (id == mSpawnId) - spawnId = mSpawnId; - else - spawnId = 0; - mSpawnId = 0; - speed = msg.readInt16(); - stunMode = msg.readInt16(); // opt1 - statusEffects = msg.readInt16(); // opt2 - statusEffects |= (static_cast( - msg.readInt16())) << 16; // option - job = msg.readInt16(); // class - - dstBeing = actorSpriteManager->findBeing(id); - - if (dstBeing && dstBeing->getType() == Being::MONSTER - && !dstBeing->isAlive()) - { - actorSpriteManager->destroy(dstBeing); - actorSpriteManager->erase(dstBeing); - dstBeing = 0; - } - - if (!dstBeing) - { - // Being with id >= 110000000 and job 0 are better - // known as ghosts, so don't create those. - if (job == 0 && id >= 110000000) - break; - - if (actorSpriteManager->isBlocked(id) == true) - break; - - dstBeing = createBeing(id, job); - - if (!dstBeing) - break; - - - if (job == 1022 && killStats) - killStats->jackoAlive(dstBeing->getId()); - } - else - { - // undeleting marked for deletion being - if (dstBeing->getType() == Being::NPC) - { - actorSpriteManager->undelete(dstBeing); - } - } - - if (dstBeing->getType() == Being::PLAYER) - dstBeing->setMoveTime(); - - if (spawnId) - { - dstBeing->setAction(Being::SPAWN); - } - else if (msg.getId() == SMSG_BEING_VISIBLE) - { - dstBeing->clearPath(); - dstBeing->setActionTime(tick_time); - dstBeing->setAction(Being::STAND); - } - - // Prevent division by 0 when calculating frame - if (speed == 0) - speed = 150; - - dstBeing->setWalkSpeed(Vector(speed, speed, 0)); - dstBeing->setSubtype(job); - if (dstBeing->getType() == ActorSprite::MONSTER && player_node) - player_node->checkNewName(dstBeing); - - hairStyle = msg.readInt16(); - weapon = msg.readInt16(); - headBottom = msg.readInt16(); - - if (msg.getId() == SMSG_BEING_MOVE) - msg.readInt32(); // server tick - - shield = msg.readInt16(); - headTop = msg.readInt16(); - headMid = msg.readInt16(); - hairColor = msg.readInt16(); - shoes = msg.readInt16(); // clothes color - "abused" as shoes - - if (dstBeing->getType() == ActorSprite::MONSTER) - { - hp = msg.readInt32(); - maxHP = msg.readInt32(); - if (hp && maxHP) - { - oldHP = dstBeing->getHP(); - if (!oldHP || oldHP > hp) - dstBeing->setHP(hp); - dstBeing->setMaxHP(maxHP); - } - gloves = 0; - guild = 0; - } - else - { - gloves = msg.readInt16(); // head dir - "abused" as gloves - guild = msg.readInt32(); // guild - msg.readInt16(); // guild emblem - } -// logger->log("being guild: " + toString(guild)); -/* - if (guild == 0) - dstBeing->clearGuilds(); - else - dstBeing->setGuild(Guild::getGuild(static_cast(guild))); -*/ - - msg.readInt16(); // manner - dstBeing->setStatusEffectBlock(32, msg.readInt16()); // opt3 - msg.readInt8(); // karma - gender = msg.readInt8(); - - if (dstBeing->getType() == ActorSprite::PLAYER) - { - dstBeing->setGender((gender == 0) - ? GENDER_FEMALE : GENDER_MALE); - // Set these after the gender, as the sprites may be gender-specific - dstBeing->setSprite(SPRITE_HAIR, hairStyle * -1, - ColorDB::getHairColor(hairColor)); - dstBeing->setSprite(SPRITE_BOTTOMCLOTHES, headBottom); - dstBeing->setSprite(SPRITE_TOPCLOTHES, headMid); - dstBeing->setSprite(SPRITE_HAT, headTop); - dstBeing->setSprite(SPRITE_SHOE, shoes); - dstBeing->setSprite(SPRITE_GLOVES, gloves); - dstBeing->setSprite(SPRITE_WEAPON, weapon, "", true); - if (!config.getBoolValue("hideShield")) - dstBeing->setSprite(SPRITE_SHIELD, shield); - } - else if (dstBeing->getType() == ActorSprite::NPC) - { - switch (gender) - { - case 2: - dstBeing->setGender(GENDER_FEMALE); - break; - case 3: - dstBeing->setGender(GENDER_MALE); - break; - default: - dstBeing->setGender(GENDER_UNSPECIFIED); - break; - } - } - - if (msg.getId() == SMSG_BEING_MOVE) - { - Uint16 srcX, srcY, dstX, dstY; - msg.readCoordinatePair(srcX, srcY, dstX, dstY); - dstBeing->setAction(Being::STAND); - dstBeing->setTileCoords(srcX, srcY); - dstBeing->setDestination(dstX, dstY); - -// if (player_node && player_node->getTarget() == dstBeing) -// player_node->targetMoved(); - } - else - { - Uint8 dir; - Uint16 x, y; - msg.readCoordinates(x, y, dir); - dstBeing->setTileCoords(x, y); - - if (job == 45 && socialWindow && outfitWindow) - { - int num = socialWindow->getPortalIndex(x, y); - if (num >= 0) - { - dstBeing->setName(keyboard.getKeyShortString( - outfitWindow->keyName(num))); - } - else - { - dstBeing->setName(""); - } - } - - dstBeing->setDirection(dir); - } - - msg.readInt8(); // unknown - msg.readInt8(); // unknown -// msg.readInt8(); // unknown / sit - msg.readInt16(); - - dstBeing->setStunMode(stunMode); - dstBeing->setStatusEffectBlock(0, static_cast( - (statusEffects >> 16) & 0xffff)); - dstBeing->setStatusEffectBlock(16, statusEffects & 0xffff); + processBeingVisibleOrMove(msg, msg.getId() == SMSG_BEING_VISIBLE); break; case SMSG_BEING_MOVE2: - /* - * A simplified movement packet, used by the - * later versions of eAthena for both mobs and - * players - */ - dstBeing = actorSpriteManager->findBeing(msg.readInt32()); - - /* - * 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) - break; - - Uint16 srcX, srcY, dstX, dstY; - msg.readCoordinatePair(srcX, srcY, dstX, dstY); - msg.readInt32(); // Server tick - - dstBeing->setAction(Being::STAND); - dstBeing->setTileCoords(srcX, srcY); - dstBeing->setDestination(dstX, dstY); - if (dstBeing->getType() == Being::PLAYER) - dstBeing->setMoveTime(); - -// if (player_node && player_node->getTarget() == dstBeing) -// { -// logger->log("SMSG_BEING_MOVE2"); -// player_node->targetMoved(); -// } - + processBeingMove2(msg); break; case SMSG_BEING_SPAWN: - // skipping this packet - mSpawnId = msg.readInt32(); // id - msg.readInt16(); // speed - msg.readInt16(); // opt1 - msg.readInt16(); // opt2 - msg.readInt16(); // option - msg.readInt16(); // disguise + processBeingSpawn(msg); break; case SMSG_BEING_REMOVE: - // A being should be removed or has died - - id = msg.readInt32(); - dstBeing = actorSpriteManager->findBeing(id); - if (!dstBeing) - break; - - player_node->followMoveTo(dstBeing, player_node->getNextDestX(), - player_node->getNextDestY()); - - // If this is player's current target, clear it. - if (dstBeing == player_node->getTarget()) - player_node->stopAttack(); - - if (msg.readInt8() == 1) - { - dstBeing->setAction(Being::DEAD); - if (dstBeing->getName() == "Jack O" && killStats) - killStats->jackoDead(id); - } - else - { - if (dstBeing->getType() == Being::PLAYER) - { - if (botCheckerWindow) - botCheckerWindow->updateList(); - if (socialWindow) - socialWindow->updateActiveList(); - } - actorSpriteManager->destroy(dstBeing); - } + processBeingRemove(msg); break; case SMSG_BEING_RESURRECT: - // A being changed mortality status - - id = msg.readInt32(); - dstBeing = actorSpriteManager->findBeing(id); - if (!dstBeing) - break; - - // If this is player's current target, clear it. - if (dstBeing == player_node->getTarget()) - player_node->stopAttack(); - - if (msg.readInt8() == 1) - dstBeing->setAction(Being::STAND); - + processBeingResurrect(msg); break; case SMSG_SKILL_DAMAGE: { - msg.readInt16(); // Skill Id - srcBeing = actorSpriteManager->findBeing(msg.readInt32()); - dstBeing = actorSpriteManager->findBeing(msg.readInt32()); - msg.readInt32(); // Server tick - msg.readInt32(); // src speed - msg.readInt32(); // dst speed - param1 = msg.readInt32(); // Damage - msg.readInt16(); // Skill level - msg.readInt16(); // Div - msg.readInt8(); // Skill hit/type (?) - if (dstBeing) - { - // Perhaps a new skill attack type should be created and used? -// if (dstSpeed) -// dstBeing->setAttackDelay(dstSpeed); - dstBeing->takeDamage(srcBeing, param1, Being::HIT); - } - if (srcBeing) - { -// if (srcSpeed) -// srcBeing->setAttackDelay(srcSpeed); - srcBeing->handleAttack(dstBeing, param1, Being::HIT); - } + processSkillDamage(msg); break; } case SMSG_BEING_ACTION: { - srcBeing = actorSpriteManager->findBeing(msg.readInt32()); - dstBeing = actorSpriteManager->findBeing(msg.readInt32()); - - msg.readInt32(); // server tick - int srcSpeed = msg.readInt32(); // src speed - msg.readInt32(); // dst speed - param1 = msg.readInt16(); - msg.readInt16(); // param 2 - type = msg.readInt8(); - msg.readInt16(); // param 3 - - switch (type) - { - case Being::HIT: // Damage - case Being::CRITICAL: // Critical Damage - case Being::MULTI: // Critical Damage - case Being::REFLECT: // Reflected Damage - case Being::FLEE: // Lucky Dodge - if (dstBeing) - { -// if (dstSpeed) -// dstBeing->setAttackDelay(dstSpeed); - dstBeing->takeDamage(srcBeing, param1, - static_cast(type)); - } - if (srcBeing) - { - if (srcSpeed && srcBeing->getType() == Being::PLAYER) - srcBeing->setAttackDelay(srcSpeed); - srcBeing->handleAttack(dstBeing, param1, - static_cast(type)); - if (srcBeing->getType() == Being::PLAYER) - srcBeing->setAttackTime(); - } - break; - - case 0x02: // Sit - if (srcBeing) - { - srcBeing->setAction(Being::SIT); - if (srcBeing->getType() == Being::PLAYER) - { - srcBeing->setMoveTime(); - if (player_node) - { - player_node->imitateAction( - srcBeing, Being::SIT); - } - } - } - break; - - case 0x03: // Stand up - if (srcBeing) - { - srcBeing->setAction(Being::STAND); - if (srcBeing->getType() == Being::PLAYER) - { - srcBeing->setMoveTime(); - if (player_node) - { - player_node->imitateAction( - srcBeing, Being::STAND); - } - } - } - break; - default: - break; -/* - logger->log("QQQ1 SMSG_BEING_ACTION:"); - if (srcBeing) - logger->log("srcBeing:" + toString(srcBeing->getId())); - if (dstBeing) - logger->log("dstBeing:" + toString(dstBeing->getId())); - logger->log("type: " + toString(type)); -*/ - } + processBeingAction(msg); break; } case SMSG_BEING_SELFEFFECT: { - if (!effectManager) - return; - - id = static_cast(msg.readInt32()); - Being* being = actorSpriteManager->findBeing(id); - if (!being) - break; - - int effectType = msg.readInt32(); - - effectManager->trigger(effectType, being); - - if (being && effectType == 3 - && being->getType() == Being::PLAYER - && socialWindow) - { //reset received damage - socialWindow->resetDamage(being->getName()); - } - + processBeingSelfEffect(msg); break; } case SMSG_BEING_EMOTION: - if (!player_node) - break; - - if (!(dstBeing = actorSpriteManager->findBeing(msg.readInt32()))) - break; - - if (player_relations.hasPermission(dstBeing, - PlayerRelation::EMOTE)) - { - unsigned char emote = msg.readInt8(); - if (emote) - { - dstBeing->setEmote(emote, EMOTION_TIME); - player_node->imitateEmote(dstBeing, emote); - } - } - if (dstBeing->getType() == Being::PLAYER) - dstBeing->setOtherTime(); - + processBeingEmotion(msg); break; case SMSG_BEING_CHANGE_LOOKS: case SMSG_BEING_CHANGE_LOOKS2: - { - /* - * SMSG_BEING_CHANGE_LOOKS (0x00c3) and - * SMSG_BEING_CHANGE_LOOKS2 (0x01d7) do basically the same - * thing. The difference is that ...LOOKS carries a single - * 8 bit value, where ...LOOKS2 carries two 16 bit values. - * - * If type = 2, then the first 16 bit value is the weapon ID, - * and the second 16 bit value is the shield ID. If no - * shield is equipped, or type is not 2, then the second - * 16 bit value will be 0. - */ - - if (!(dstBeing = actorSpriteManager->findBeing(msg.readInt32()))) - break; - - int type = msg.readInt8(); - int id = 0; - int id2 = 0; - std::string color; - - if (msg.getId() == SMSG_BEING_CHANGE_LOOKS) - { - id = msg.readInt8(); - id2 = 1; // default color - } - else - { // SMSG_BEING_CHANGE_LOOKS2 - id = msg.readInt16(); - if (type == 2 || serverVersion > 0) - id2 = msg.readInt16(); - else - id2 = 1; - color = ""; - } - - if (dstBeing->getType() == Being::PLAYER) - dstBeing->setOtherTime(); - - if (!player_node) - break; - - switch (type) - { - case 0: // change race - dstBeing->setSubtype(static_cast(id)); - break; - case 1: // eAthena LOOK_HAIR - dstBeing->setSpriteID(SPRITE_HAIR, id *-1); - break; - case 2: // Weapon ID in id, Shield ID in id2 - dstBeing->setSprite(SPRITE_WEAPON, id, "", 1, true); - if (!config.getBoolValue("hideShield")) - dstBeing->setSprite(SPRITE_SHIELD, id2); - player_node->imitateOutfit(dstBeing, SPRITE_SHIELD); - break; - case 3: // Change lower headgear for eAthena, pants for us - dstBeing->setSprite(SPRITE_BOTTOMCLOTHES, id, color, - static_cast(id2)); - player_node->imitateOutfit(dstBeing, SPRITE_BOTTOMCLOTHES); - break; - case 4: // Change upper headgear for eAthena, hat for us - dstBeing->setSprite(SPRITE_HAT, id, color, - static_cast(id2)); - player_node->imitateOutfit(dstBeing, SPRITE_HAT); - break; - case 5: // Change middle headgear for eathena, armor for us - dstBeing->setSprite(SPRITE_TOPCLOTHES, id, color, - static_cast(id2)); - player_node->imitateOutfit(dstBeing, SPRITE_TOPCLOTHES); - break; - case 6: // eAthena LOOK_HAIR_COLOR - dstBeing->setSpriteColor(SPRITE_HAIR, - ColorDB::getHairColor(id)); - break; - case 8: // eAthena LOOK_SHIELD - if (!config.getBoolValue("hideShield")) - { - dstBeing->setSprite(SPRITE_SHIELD, id, color, - static_cast(id2)); - } - player_node->imitateOutfit(dstBeing, SPRITE_SHIELD); - break; - case 9: // eAthena LOOK_SHOES - dstBeing->setSprite(SPRITE_SHOE, id, color, - static_cast(id2)); - player_node->imitateOutfit(dstBeing, SPRITE_SHOE); - break; - case 10: // LOOK_GLOVES - dstBeing->setSprite(SPRITE_GLOVES, id, color, - static_cast(id2)); - player_node->imitateOutfit(dstBeing, SPRITE_GLOVES); - break; - case 11: // LOOK_CAPE - dstBeing->setSprite(SPRITE_CAPE, id, color, - static_cast(id2)); - player_node->imitateOutfit(dstBeing, SPRITE_CAPE); - break; - case 12: - dstBeing->setSprite(SPRITE_MISC1, id, color, - static_cast(id2)); - player_node->imitateOutfit(dstBeing, SPRITE_MISC1); - break; - case 13: - dstBeing->setSprite(SPRITE_MISC2, id, color, - static_cast(id2)); - player_node->imitateOutfit(dstBeing, SPRITE_MISC2); - break; - case 14: - dstBeing->setSprite(SPRITE_EVOL1, id, color, - static_cast(id2)); - player_node->imitateOutfit(dstBeing, SPRITE_EVOL1); - break; - case 15: - dstBeing->setSprite(SPRITE_EVOL2, id, color, - static_cast(id2)); - player_node->imitateOutfit(dstBeing, SPRITE_EVOL2); - break; - default: - logger->log("QQQ3 CHANGE_LOOKS: unsupported type: " - "%d, id: %d", type, id); - if (dstBeing) - { - logger->log("ID: " + toString(dstBeing->getId())); - logger->log("name: " + toString(dstBeing->getName())); - } - break; - } - } + processBeingChangeLook(msg, + msg.getId() == SMSG_BEING_CHANGE_LOOKS2); break; case SMSG_BEING_NAME_RESPONSE: - { - int beingId = msg.readInt32(); - if ((dstBeing = actorSpriteManager->findBeing(beingId))) - { - if (beingId == player_node->getId()) - { - player_node->pingResponse(); - } - else - { - dstBeing->setName(msg.readString(24)); - dstBeing->updateGuild(); - dstBeing->addToCache(); - - if (dstBeing->getType() == Being::PLAYER) - dstBeing->updateColors(); - - if (player_node) - { - Party *party = player_node->getParty(); - if (party && party->isMember(dstBeing->getId())) - { - PartyMember *member = party->getMember( - dstBeing->getId()); - - if (member) - member->setName(dstBeing->getName()); - } - player_node->checkNewName(dstBeing); - } - } - } - } + processNameResponse(msg); break; + case SMSG_BEING_NAME_RESPONSE2: - { - int len = msg.readInt16(); - int beingId = msg.readInt32(); - std::string str = msg.readString(len - 8); - if ((dstBeing = actorSpriteManager->findBeing(beingId))) - { - if (beingId == player_node->getId()) - { - player_node->pingResponse(); - } - else - { - dstBeing->setName(str); - dstBeing->updateGuild(); - dstBeing->addToCache(); - - if (dstBeing->getType() == Being::PLAYER) - dstBeing->updateColors(); - - if (player_node) - { - Party *party = player_node->getParty(); - if (party && party->isMember(dstBeing->getId())) - { - PartyMember *member = party->getMember( - dstBeing->getId()); - - if (member) - member->setName(dstBeing->getName()); - } - player_node->checkNewName(dstBeing); - } - } - } - } + processNameResponse2(msg); break; + case SMSG_BEING_IP_RESPONSE: - { - if ((dstBeing = actorSpriteManager->findBeing( - msg.readInt32()))) - { - dstBeing->setIp(ipToString(msg.readInt32())); - } - } + processIpResponse(msg); break; + case SMSG_SOLVE_CHAR_NAME: - { - logger->log1("SMSG_SOLVE_CHAR_NAME"); - logger->log(toString(msg.readInt32())); - logger->log(msg.readString(24)); - } break; + case SMSG_PLAYER_GUILD_PARTY_INFO: - if ((dstBeing = actorSpriteManager->findBeing(msg.readInt32()))) - { - dstBeing->setPartyName(msg.readString(24)); - dstBeing->setGuildName(msg.readString(24)); - dstBeing->setGuildPos(msg.readString(24)); - dstBeing->addToCache(); - msg.readString(24); // Discard this - } + processPlayerGuilPartyInfo(msg); break; - case SMSG_BEING_CHANGE_DIRECTION: - { - if (!(dstBeing = actorSpriteManager->findBeing(msg.readInt32()))) - break; - - msg.readInt16(); // unused - unsigned char dir = msg.readInt8(); - dstBeing->setDirection(dir); - if (player_node) - player_node->imitateDirection(dstBeing, dir); + case SMSG_BEING_CHANGE_DIRECTION: + processBeingChangeDirection(msg); break; - } + case SMSG_PLAYER_UPDATE_1: case SMSG_PLAYER_UPDATE_2: case SMSG_PLAYER_MOVE: - if (!actorSpriteManager || !player_node) - break; + int type; + switch (msg.getId()) + { + case SMSG_PLAYER_UPDATE_1: + type = 1; + break; + case SMSG_PLAYER_UPDATE_2: + type = 2; + break; + case SMSG_PLAYER_MOVE: + type = 3; + break; + default: + return; + } + processPlayerMoveUpdate(msg, type); - // An update about a player, potentially including movement. - id = msg.readInt32(); - speed = msg.readInt16(); - stunMode = msg.readInt16(); // opt1; Aethyra use this as cape - statusEffects = msg.readInt16(); // opt2; Aethyra use this as misc1 - statusEffects |= (static_cast(msg.readInt16())) - << 16; // status.options; Aethyra uses this as misc2 - job = msg.readInt16(); + break; + case SMSG_PLAYER_STOP: + processPlayerStop(msg); + break; - dstBeing = actorSpriteManager->findBeing(id); + case SMSG_PLAYER_MOVE_TO_ATTACK: + processPlayerMoveToAttack(msg); + break; - if (!dstBeing) - { - if (actorSpriteManager->isBlocked(id) == true) - break; + case SMSG_PLAYER_STATUS_CHANGE: + processPlaterStatusChange(msg); + break; - dstBeing = createBeing(id, job); + case SMSG_BEING_STATUS_CHANGE: + processBeingStatusChange(msg); + break; - if (!dstBeing) - break; - } + case SMSG_SKILL_CASTING: + processSkilCasting(msg); + break; - dir = dstBeing->getDirectionDelayed(); - if (dir) - { - if (dir != dstBeing->getDirection()) - dstBeing->setDirection(dir); - } + case SMSG_SKILL_CAST_CANCEL: + msg.readInt32(); // id + break; - if (Party *party = player_node->getParty()) - { - if (party->isMember(id)) - dstBeing->setParty(party); - } + case SMSG_SKILL_NO_DAMAGE: + processSkillNoDamage(msg); + break; - dstBeing->setWalkSpeed(Vector(speed, speed, 0)); - dstBeing->setSubtype(job); - hairStyle = msg.readInt16(); - weapon = msg.readInt16(); - shield = msg.readInt16(); - headBottom = msg.readInt16(); + case SMSG_PVP_MAP_MODE: + processPvpMapMode(msg); + break; - if (msg.getId() == SMSG_PLAYER_MOVE) - msg.readInt32(); // server tick + case SMSG_PVP_SET: + processPvpSet(msg); + break; + + default: + break; + } +} + +void BeingHandler::undress(Being *being) +{ + being->setSprite(SPRITE_BOTTOMCLOTHES, 0); + being->setSprite(SPRITE_TOPCLOTHES, 0); + being->setSprite(SPRITE_HAT, 0); + being->setSprite(SPRITE_SHOE, 0); + being->setSprite(SPRITE_GLOVES, 0); +// being->setSprite(SPRITE_WEAPON, 0, "", true); +} - headTop = msg.readInt16(); - headMid = msg.readInt16(); - hairColor = msg.readInt16(); +void BeingHandler::processBeingChangeLook(Net::MessageIn &msg, bool look2) +{ + if (!actorSpriteManager) + return; - colors[0] = msg.readInt8(); - colors[1] = msg.readInt8(); - colors[2] = msg.readInt8(); + Being *dstBeing; + /* + * SMSG_BEING_CHANGE_LOOKS (0x00c3) and + * SMSG_BEING_CHANGE_LOOKS2 (0x01d7) do basically the same + * thing. The difference is that ...LOOKS carries a single + * 8 bit value, where ...LOOKS2 carries two 16 bit values. + * + * If type = 2, then the first 16 bit value is the weapon ID, + * and the second 16 bit value is the shield ID. If no + * shield is equipped, or type is not 2, then the second + * 16 bit value will be 0. + */ + + if (!(dstBeing = actorSpriteManager->findBeing(msg.readInt32()))) + return; - msg.readInt8(); //unused -// shoes = msg.readInt16(); -// gloves = msg.readInt16(); //sd->head_dir + int type = msg.readInt8(); + int id = 0; + int id2 = 0; + std::string color; - guild = msg.readInt32(); // guild + if (!look2) + { + id = msg.readInt8(); + id2 = 1; // default color + } + else + { // SMSG_BEING_CHANGE_LOOKS2 + id = msg.readInt16(); + if (type == 2 || serverVersion > 0) + id2 = msg.readInt16(); + else + id2 = 1; + color = ""; + } - if (guild == 0) - dstBeing->clearGuilds(); - else - dstBeing->setGuild(Guild::getGuild(static_cast(guild))); + if (dstBeing->getType() == Being::PLAYER) + dstBeing->setOtherTime(); - msg.readInt16(); // emblem - msg.readInt16(); // manner - dstBeing->setStatusEffectBlock(32, msg.readInt16()); // opt3 - msg.readInt8(); // karma - dstBeing->setGender((msg.readInt8() == 0) - ? GENDER_FEMALE : GENDER_MALE); + if (!player_node) + return; - // Set these after the gender, as the sprites may be gender-specific - dstBeing->setSprite(SPRITE_WEAPON, weapon, "", 1, true); + switch (type) + { + case 0: // change race + dstBeing->setSubtype(static_cast(id)); + break; + case 1: // eAthena LOOK_HAIR + dstBeing->setSpriteID(SPRITE_HAIR, id *-1); + break; + case 2: // Weapon ID in id, Shield ID in id2 + dstBeing->setSprite(SPRITE_WEAPON, id, "", 1, true); + if (!config.getBoolValue("hideShield")) + dstBeing->setSprite(SPRITE_SHIELD, id2); + player_node->imitateOutfit(dstBeing, SPRITE_SHIELD); + break; + case 3: // Change lower headgear for eAthena, pants for us + dstBeing->setSprite(SPRITE_BOTTOMCLOTHES, id, color, + static_cast(id2)); + player_node->imitateOutfit(dstBeing, SPRITE_BOTTOMCLOTHES); + break; + case 4: // Change upper headgear for eAthena, hat for us + dstBeing->setSprite(SPRITE_HAT, id, color, + static_cast(id2)); + player_node->imitateOutfit(dstBeing, SPRITE_HAT); + break; + case 5: // Change middle headgear for eathena, armor for us + dstBeing->setSprite(SPRITE_TOPCLOTHES, id, color, + static_cast(id2)); + player_node->imitateOutfit(dstBeing, SPRITE_TOPCLOTHES); + break; + case 6: // eAthena LOOK_HAIR_COLOR + dstBeing->setSpriteColor(SPRITE_HAIR, + ColorDB::getHairColor(id)); + break; + case 8: // eAthena LOOK_SHIELD if (!config.getBoolValue("hideShield")) - dstBeing->setSprite(SPRITE_SHIELD, shield); - //dstBeing->setSprite(SPRITE_SHOE, shoes); - if (serverVersion > 0) { - dstBeing->setSprite(SPRITE_BOTTOMCLOTHES, headBottom, - "", colors[0]); - dstBeing->setSprite(SPRITE_TOPCLOTHES, headMid, "", colors[2]); - dstBeing->setSprite(SPRITE_HAT, headTop, "", colors[1]); + dstBeing->setSprite(SPRITE_SHIELD, id, color, + static_cast(id2)); } - else + player_node->imitateOutfit(dstBeing, SPRITE_SHIELD); + break; + case 9: // eAthena LOOK_SHOES + dstBeing->setSprite(SPRITE_SHOE, id, color, + static_cast(id2)); + player_node->imitateOutfit(dstBeing, SPRITE_SHOE); + break; + case 10: // LOOK_GLOVES + dstBeing->setSprite(SPRITE_GLOVES, id, color, + static_cast(id2)); + player_node->imitateOutfit(dstBeing, SPRITE_GLOVES); + break; + case 11: // LOOK_CAPE + dstBeing->setSprite(SPRITE_CAPE, id, color, + static_cast(id2)); + player_node->imitateOutfit(dstBeing, SPRITE_CAPE); + break; + case 12: + dstBeing->setSprite(SPRITE_MISC1, id, color, + static_cast(id2)); + player_node->imitateOutfit(dstBeing, SPRITE_MISC1); + break; + case 13: + dstBeing->setSprite(SPRITE_MISC2, id, color, + static_cast(id2)); + player_node->imitateOutfit(dstBeing, SPRITE_MISC2); + break; + case 14: + dstBeing->setSprite(SPRITE_EVOL1, id, color, + static_cast(id2)); + player_node->imitateOutfit(dstBeing, SPRITE_EVOL1); + break; + case 15: + dstBeing->setSprite(SPRITE_EVOL2, id, color, + static_cast(id2)); + player_node->imitateOutfit(dstBeing, SPRITE_EVOL2); + break; + default: + logger->log("QQQ3 CHANGE_LOOKS: unsupported type: " + "%d, id: %d", type, id); + if (dstBeing) { - dstBeing->setSprite(SPRITE_BOTTOMCLOTHES, headBottom); - dstBeing->setSprite(SPRITE_TOPCLOTHES, headMid); - dstBeing->setSprite(SPRITE_HAT, headTop); + logger->log("ID: " + toString(dstBeing->getId())); + logger->log("name: " + toString(dstBeing->getName())); } - //dstBeing->setSprite(SPRITE_GLOVES, gloves); - //dstBeing->setSprite(SPRITE_CAPE, cape); - //dstBeing->setSprite(SPRITE_MISC1, misc1); - //dstBeing->setSprite(SPRITE_MISC2, misc2); - dstBeing->setSprite(SPRITE_HAIR, hairStyle * -1, - ColorDB::getHairColor(hairColor)); - - player_node->imitateOutfit(dstBeing); + break; + } +} - if (msg.getId() == SMSG_PLAYER_MOVE) - { - Uint16 srcX, srcY, dstX, dstY; - msg.readCoordinatePair(srcX, srcY, dstX, dstY); +void BeingHandler::processNameResponse2(Net::MessageIn &msg) +{ + if (!actorSpriteManager || !player_node) + return; - player_node->followMoveTo(dstBeing, srcX, srcY, dstX, dstY); + Being *dstBeing; - dstBeing->setTileCoords(srcX, srcY); - dstBeing->setDestination(dstX, dstY); + int len = msg.readInt16(); + int beingId = msg.readInt32(); + std::string str = msg.readString(len - 8); + if ((dstBeing = actorSpriteManager->findBeing(beingId))) + { + if (beingId == player_node->getId()) + { + player_node->pingResponse(); + } + else + { + dstBeing->setName(str); + dstBeing->updateGuild(); + dstBeing->addToCache(); - // because server dont send direction in move packet, - // we fixing it + if (dstBeing->getType() == Being::PLAYER) + dstBeing->updateColors(); - if (srcX != dstX || srcY != dstY) + if (player_node) + { + Party *party = player_node->getParty(); + if (party && party->isMember(dstBeing->getId())) { - int dir = dstBeing->calcDirection(dstX, dstY); + PartyMember *member = party->getMember( + dstBeing->getId()); - if (dir && dstBeing->getDirection() != dir) - dstBeing->setDirectionDelayed(static_cast(dir)); + if (member) + member->setName(dstBeing->getName()); } - - if (player_node->getCurrentAction() != Being::STAND) - player_node->imitateAction(dstBeing, Being::STAND); - if (player_node->getDirection() != dstBeing->getDirection()) - { - player_node->imitateDirection(dstBeing, - dstBeing->getDirection()); - } - } - else - { - Uint8 dir; - Uint16 x, y; - msg.readCoordinates(x, y, dir); - dstBeing->setTileCoords(x, y); - dstBeing->setDirection(dir); - - player_node->imitateDirection(dstBeing, dir); + player_node->checkNewName(dstBeing); } + } + } +} - gmstatus = msg.readInt16(); +void BeingHandler::processPlayerMoveUpdate(Net::MessageIn &msg, int msgType) +{ + if (!actorSpriteManager || !player_node) + return; - if (gmstatus & 0x80) - dstBeing->setGM(true); + Uint16 headTop, headMid, headBottom; + Uint16 weapon, shield; + Uint16 gmstatus; + int level; + int guild; + Being *dstBeing; + int hairStyle, hairColor; + unsigned char colors[9]; + Uint8 dir; - if (msg.getId() == SMSG_PLAYER_UPDATE_1) - { - int type = msg.readInt8(); - switch (type) - { - case 0: - dstBeing->setAction(Being::STAND); - player_node->imitateAction(dstBeing, Being::STAND); - break; - - case 1: - dstBeing->setAction(Being::DEAD); - break; - - case 2: - dstBeing->setAction(Being::SIT); - player_node->imitateAction(dstBeing, Being::SIT); - break; - - default: - //need set stay state? - logger->log("QQQ2 SMSG_PLAYER_UPDATE_1:" - + toString(id) + " " + toString(type)); - if (dstBeing) - { - logger->log("dstBeing id:" - + toString(dstBeing->getId())); - logger->log("dstBeing name:" - + dstBeing->getName()); - } - break; + // An update about a player, potentially including movement. + int id = msg.readInt32(); + short speed = msg.readInt16(); + Uint16 stunMode = msg.readInt16(); // opt1; Aethyra use this as cape + Uint32 statusEffects = msg.readInt16(); // opt2; Aethyra use this as misc1 + statusEffects |= (static_cast(msg.readInt16())) + << 16; // status.options; Aethyra uses this as misc2 + short job = msg.readInt16(); - } - } - else if (msg.getId() == SMSG_PLAYER_MOVE) - { - msg.readInt8(); // unknown - } + dstBeing = actorSpriteManager->findBeing(id); - level = msg.readInt8(); // Lv - if (level) - dstBeing->setLevel(level); + if (!dstBeing) + { + if (actorSpriteManager->isBlocked(id) == true) + return; - msg.readInt8(); // unknown + dstBeing = createBeing(id, job); - if (dstBeing->getType() != Being::PLAYER - || msg.getId() != SMSG_PLAYER_MOVE) - { - dstBeing->setActionTime(tick_time); -// dstBeing->reset(); - } + if (!dstBeing) + return; + } - dstBeing->setStunMode(stunMode); - dstBeing->setStatusEffectBlock(0, static_cast( - (statusEffects >> 16) & 0xffff)); - dstBeing->setStatusEffectBlock(16, statusEffects & 0xffff); + dir = dstBeing->getDirectionDelayed(); + if (dir) + { + if (dir != dstBeing->getDirection()) + dstBeing->setDirection(dir); + } - if (msg.getId() == SMSG_PLAYER_MOVE - && dstBeing->getType() == Being::PLAYER) - { - dstBeing->setMoveTime(); - } + if (Party *party = player_node->getParty()) + { + if (party->isMember(id)) + dstBeing->setParty(party); + } - break; - case SMSG_PLAYER_STOP: - /* - * Instruction from server to stop walking at x, y. - * - * Some people like having this enabled. Others absolutely - * despise it. So I'm setting to so that it only affects the - * local player if the person has set a key "EnableSync" to "1" - * in their config.xml file. - * - * This packet will be honored for all other beings, regardless - * of the config setting. - */ - - id = msg.readInt32(); - - if (mSync || id != player_node->getId()) - { - dstBeing = actorSpriteManager->findBeing(id); - if (dstBeing) - { - Uint16 x, y; - x = msg.readInt16(); - y = msg.readInt16(); - dstBeing->setTileCoords(x, y); - if (dstBeing->getCurrentAction() == Being::MOVE) - dstBeing->setAction(Being::STAND); - } - } - break; + dstBeing->setWalkSpeed(Vector(speed, speed, 0)); + dstBeing->setSubtype(job); + hairStyle = msg.readInt16(); + weapon = msg.readInt16(); + shield = msg.readInt16(); + headBottom = msg.readInt16(); - case SMSG_PLAYER_MOVE_TO_ATTACK: - /* - * This is an *advisory* message, telling the client that - * it needs to move the character before attacking - * a target (out of range, obstruction in line of fire). - * We can safely ignore this... - */ - if (player_node) - player_node->fixAttackTarget(); - break; + if (msgType == 3) + msg.readInt32(); // server tick - case SMSG_PLAYER_STATUS_CHANGE: - // Change in players' flags + headTop = msg.readInt16(); + headMid = msg.readInt16(); + hairColor = msg.readInt16(); - id = msg.readInt32(); - dstBeing = actorSpriteManager->findBeing(id); - if (!dstBeing) - break; + colors[0] = msg.readInt8(); + colors[1] = msg.readInt8(); + colors[2] = msg.readInt8(); - stunMode = msg.readInt16(); - statusEffects = msg.readInt16(); - statusEffects |= (static_cast(msg.readInt16())) << 16; - msg.readInt8(); // Unused? + msg.readInt8(); //unused +// shoes = msg.readInt16(); +// gloves = msg.readInt16(); //sd->head_dir - dstBeing->setStunMode(stunMode); - dstBeing->setStatusEffectBlock(0, static_cast( - (statusEffects >> 16) & 0xffff)); - dstBeing->setStatusEffectBlock(16, statusEffects & 0xffff); - break; + guild = msg.readInt32(); // guild + + if (guild == 0) + dstBeing->clearGuilds(); + else + dstBeing->setGuild(Guild::getGuild(static_cast(guild))); + + msg.readInt16(); // emblem + msg.readInt16(); // manner + dstBeing->setStatusEffectBlock(32, msg.readInt16()); // opt3 + msg.readInt8(); // karma + dstBeing->setGender((msg.readInt8() == 0) + ? GENDER_FEMALE : GENDER_MALE); + + // Set these after the gender, as the sprites may be gender-specific + dstBeing->setSprite(SPRITE_WEAPON, weapon, "", 1, true); + if (!config.getBoolValue("hideShield")) + dstBeing->setSprite(SPRITE_SHIELD, shield); + //dstBeing->setSprite(SPRITE_SHOE, shoes); + if (serverVersion > 0) + { + dstBeing->setSprite(SPRITE_BOTTOMCLOTHES, headBottom, + "", colors[0]); + dstBeing->setSprite(SPRITE_TOPCLOTHES, headMid, "", colors[2]); + dstBeing->setSprite(SPRITE_HAT, headTop, "", colors[1]); + } + else + { + dstBeing->setSprite(SPRITE_BOTTOMCLOTHES, headBottom); + dstBeing->setSprite(SPRITE_TOPCLOTHES, headMid); + dstBeing->setSprite(SPRITE_HAT, headTop); + } + //dstBeing->setSprite(SPRITE_GLOVES, gloves); + //dstBeing->setSprite(SPRITE_CAPE, cape); + //dstBeing->setSprite(SPRITE_MISC1, misc1); + //dstBeing->setSprite(SPRITE_MISC2, misc2); + dstBeing->setSprite(SPRITE_HAIR, hairStyle * -1, + ColorDB::getHairColor(hairColor)); - case SMSG_BEING_STATUS_CHANGE: - // Status change - status = msg.readInt16(); - id = msg.readInt32(); - flag = msg.readInt8(); // 0: stop, 1: start + player_node->imitateOutfit(dstBeing); - dstBeing = actorSpriteManager->findBeing(id); - if (dstBeing) - dstBeing->setStatusEffect(status, flag); - break; + if (msgType == 3) + { + Uint16 srcX, srcY, dstX, dstY; + msg.readCoordinatePair(srcX, srcY, dstX, dstY); - case SMSG_SKILL_CASTING: - msg.readInt32(); // src id - msg.readInt32(); // dst id - msg.readInt16(); // dst x - msg.readInt16(); // dst y - msg.readInt16(); // skill num - msg.readInt32(); // skill get pl - msg.readInt32(); // cast time - break; + player_node->followMoveTo(dstBeing, srcX, srcY, dstX, dstY); - case SMSG_SKILL_CAST_CANCEL: - msg.readInt32(); // id - break; + dstBeing->setTileCoords(srcX, srcY); + dstBeing->setDestination(dstX, dstY); - case SMSG_SKILL_NO_DAMAGE: - msg.readInt16(); // skill id - msg.readInt16(); // heal - msg.readInt32(); // dst id - msg.readInt32(); // src id - msg.readInt8(); // fail - break; + // because server dont send direction in move packet, + // we fixing it - case SMSG_PVP_MAP_MODE: + if (srcX != dstX || srcY != dstY) { - Game *game = Game::instance(); - if (!game) - break; + int dir = dstBeing->calcDirection(dstX, dstY); - Map *map = game->getCurrentMap(); - if (map) - map->setPvpMode(msg.readInt16()); - break; + if (dir && dstBeing->getDirection() != dir) + dstBeing->setDirectionDelayed(static_cast(dir)); } - case SMSG_PVP_SET: + if (player_node->getCurrentAction() != Being::STAND) + player_node->imitateAction(dstBeing, Being::STAND); + if (player_node->getDirection() != dstBeing->getDirection()) { - int id = msg.readInt32(); // id - int rank = msg.readInt32(); // rank - msg.readInt32(); // num - if (actorSpriteManager) - { - dstBeing = actorSpriteManager->findBeing(id); + player_node->imitateDirection(dstBeing, + dstBeing->getDirection()); + } + } + else + { + Uint8 dir; + Uint16 x, y; + msg.readCoordinates(x, y, dir); + dstBeing->setTileCoords(x, y); + dstBeing->setDirection(dir); + + player_node->imitateDirection(dstBeing, dir); + } + + gmstatus = msg.readInt16(); + + if (gmstatus & 0x80) + dstBeing->setGM(true); + + if (msgType == 1) + { + int type = msg.readInt8(); + switch (type) + { + case 0: + dstBeing->setAction(Being::STAND); + player_node->imitateAction(dstBeing, Being::STAND); + break; + + case 1: + dstBeing->setAction(Being::DEAD); + break; + + case 2: + dstBeing->setAction(Being::SIT); + player_node->imitateAction(dstBeing, Being::SIT); + break; + + default: + //need set stay state? + logger->log("QQQ2 SMSG_PLAYER_UPDATE_1:" + + toString(id) + " " + toString(type)); if (dstBeing) - dstBeing->setPvpRank(rank); - } - break; + { + logger->log("dstBeing id:" + + toString(dstBeing->getId())); + logger->log("dstBeing name:" + + dstBeing->getName()); + } + break; + } + } + else if (msgType == 3) + { + msg.readInt8(); // unknown + } - default: - break; + level = msg.readInt8(); // Lv + if (level) + dstBeing->setLevel(level); + + msg.readInt8(); // unknown + + if (dstBeing->getType() != Being::PLAYER + || msgType != 3) + { + dstBeing->setActionTime(tick_time); +// dstBeing->reset(); } -} -void BeingHandler::undress(Being *being) -{ - being->setSprite(SPRITE_BOTTOMCLOTHES, 0); - being->setSprite(SPRITE_TOPCLOTHES, 0); - being->setSprite(SPRITE_HAT, 0); - being->setSprite(SPRITE_SHOE, 0); - being->setSprite(SPRITE_GLOVES, 0); -// being->setSprite(SPRITE_WEAPON, 0, "", true); + dstBeing->setStunMode(stunMode); + dstBeing->setStatusEffectBlock(0, static_cast( + (statusEffects >> 16) & 0xffff)); + dstBeing->setStatusEffectBlock(16, statusEffects & 0xffff); + + if (msgType == 3 && dstBeing->getType() == Being::PLAYER) + dstBeing->setMoveTime(); } + + } // namespace TmwAthena diff --git a/src/net/tmwa/beinghandler.h b/src/net/tmwa/beinghandler.h index aa278b1d6..5e741aaa8 100644 --- a/src/net/tmwa/beinghandler.h +++ b/src/net/tmwa/beinghandler.h @@ -26,12 +26,14 @@ #include "net/beinghandler.h" #include "net/net.h" +#include "net/ea/beinghandler.h" + #include "net/tmwa/messagehandler.h" namespace TmwAthena { -class BeingHandler : public MessageHandler, public Net::BeingHandler +class BeingHandler : public MessageHandler, public Ea::BeingHandler { public: BeingHandler(bool enableSync); @@ -42,10 +44,12 @@ class BeingHandler : public MessageHandler, public Net::BeingHandler virtual void undress(Being *being); - private: - // Should we honor server "Stop Walking" packets - bool mSync; - int mSpawnId; + protected: + virtual void processBeingChangeLook(Net::MessageIn &msg, bool look2); + + void processNameResponse2(Net::MessageIn &msg); + + virtual void processPlayerMoveUpdate(Net::MessageIn &msg, int type); }; } // namespace TmwAthena diff --git a/src/net/tmwa/buysellhandler.cpp b/src/net/tmwa/buysellhandler.cpp index 5113f0303..0b839a3f5 100644 --- a/src/net/tmwa/buysellhandler.cpp +++ b/src/net/tmwa/buysellhandler.cpp @@ -63,7 +63,6 @@ BuySellHandler::BuySellHandler() SMSG_NPC_SELL_RESPONSE, 0 }; - mNpcId = 0; handledMessages = _messages; buySellHandler = this; mBuyDialog = 0; @@ -71,181 +70,75 @@ BuySellHandler::BuySellHandler() void BuySellHandler::handleMessage(Net::MessageIn &msg) { - int n_items; - switch (msg.getId()) { case SMSG_NPC_BUY_SELL_CHOICE: - if (!BuySellDialog::isActive()) - { - mNpcId = msg.readInt32(); - new BuySellDialog(mNpcId); - } + processNpcBuySellChoice(msg); break; case SMSG_NPC_BUY: - { - msg.readInt16(); // length - int sz = 11; - if (serverVersion > 0) - sz += 1; - n_items = (msg.getLength() - 4) / sz; - mBuyDialog = new BuyDialog(mNpcId); - mBuyDialog->setMoney(PlayerInfo::getAttribute(MONEY)); - - for (int k = 0; k < n_items; k++) - { - int value = msg.readInt32(); - msg.readInt32(); // DCvalue - msg.readInt8(); // type - int itemId = msg.readInt16(); - unsigned char color = 1; - if (serverVersion > 0) - color = msg.readInt8(); - mBuyDialog->addItem(itemId, color, 0, value); - } + processNpcBuy(msg); break; - } case SMSG_NPC_SELL: - msg.readInt16(); // length - n_items = (msg.getLength() - 4) / 10; - if (n_items > 0) - { - SellDialog *dialog = new SellDialog(mNpcId); - dialog->setMoney(PlayerInfo::getAttribute(MONEY)); - - for (int k = 0; k < n_items; k++) - { - int index = msg.readInt16() - INVENTORY_OFFSET; - int value = msg.readInt32(); - msg.readInt32(); // OCvalue - - Item *item = PlayerInfo::getInventory()->getItem(index); - - if (item && !(item->isEquipped())) - dialog->addItem(item, value); - } - } - else - { - SERVER_NOTICE(_("Nothing to sell.")) - } + processNpcSell(msg, INVENTORY_OFFSET); break; case SMSG_NPC_BUY_RESPONSE: - if (msg.readInt8() == 0) - { - SERVER_NOTICE(_("Thanks for buying.")) - } - else - { - // Reset player money since buy dialog already assumed purchase - // would go fine - if (mBuyDialog) - mBuyDialog->setMoney(PlayerInfo::getAttribute(MONEY)); - SERVER_NOTICE(_("Unable to buy.")) - } + processNpcBuyResponse(msg); break; case SMSG_NPC_SELL_RESPONSE: - switch (msg.readInt8()) - { - case 0: - SERVER_NOTICE(_("Thanks for selling.")) - break; - case 1: - default: - SERVER_NOTICE(_("Unable to sell.")) - break; - case 2: - SERVER_NOTICE(_("Unable to sell while trading.")) - break; - case 3: - SERVER_NOTICE(_("Unable to sell unsellable item.")) - break; - } - default: + processNpcSellResponse(msg); break; - } - -} - -void BuySellHandler::requestSellList(std::string nick) -{ - if (nick.empty() != 0 || !shopWindow) - return; - std::string data = "!selllist " + toString(tick_time); - shopWindow->setAcceptPlayer(nick); - - if (config.getBoolValue("hideShopMessages")) - { - Net::getChatHandler()->privateMessage(nick, data); - } - else - { - if (chatWindow) - chatWindow->whisper(nick, data, BY_PLAYER); + default: + break; } -//was true -} -void BuySellHandler::requestBuyList(std::string nick) -{ - if (nick.empty() || !shopWindow) - return; - - std::string data = "!buylist " + toString(tick_time); - shopWindow->setAcceptPlayer(nick); - - if (config.getBoolValue("hideShopMessages")) - { - Net::getChatHandler()->privateMessage(nick, data); - } - else - { - if (chatWindow) - chatWindow->whisper(nick, data, BY_PLAYER); - } -//was true } -void BuySellHandler::sendBuyRequest(std::string nick, ShopItem* item, - int amount) +void BuySellHandler::processNpcBuy(Net::MessageIn &msg) { - if (!chatWindow || nick.empty() || !item || - amount < 1 || amount > item->getQuantity()) + msg.readInt16(); // length + int sz = 11; + if (serverVersion > 0) + sz += 1; + int n_items = (msg.getLength() - 4) / sz; + mBuyDialog = new BuyDialog(mNpcId); + mBuyDialog->setMoney(PlayerInfo::getAttribute(MONEY)); + + for (int k = 0; k < n_items; k++) { - return; + int value = msg.readInt32(); + msg.readInt32(); // DCvalue + msg.readInt8(); // type + int itemId = msg.readInt16(); + unsigned char color = 1; + if (serverVersion > 0) + color = msg.readInt8(); + mBuyDialog->addItem(itemId, color, 0, value); } - std::string data = strprintf("!buyitem %d %d %d", - item->getId(), item->getPrice(), amount); - - if (config.getBoolValue("hideShopMessages")) - Net::getChatHandler()->privateMessage(nick, data); - else - chatWindow->whisper(nick, data, BY_PLAYER); -//was true } -void BuySellHandler::sendSellRequest(std::string nick, ShopItem* item, - int amount) +void BuySellHandler::processNpcSellResponse(Net::MessageIn &msg) { - if (!chatWindow || nick.empty() || !item || - amount < 1 || amount > item->getQuantity()) + switch (msg.readInt8()) { - return; + case 0: + SERVER_NOTICE(_("Thanks for selling.")) + break; + case 1: + default: + SERVER_NOTICE(_("Unable to sell.")) + break; + case 2: + SERVER_NOTICE(_("Unable to sell while trading.")) + break; + case 3: + SERVER_NOTICE(_("Unable to sell unsellable item.")) + break; } - - std::string data = strprintf("!sellitem %d %d %d", - item->getId(), item->getPrice(), amount); - - if (config.getBoolValue("hideShopMessages")) - Net::getChatHandler()->privateMessage(nick, data); - else - chatWindow->whisper(nick, data, BY_PLAYER); -//was true } } // namespace TmwAthena diff --git a/src/net/tmwa/buysellhandler.h b/src/net/tmwa/buysellhandler.h index 09aad0ec2..65067f7e0 100644 --- a/src/net/tmwa/buysellhandler.h +++ b/src/net/tmwa/buysellhandler.h @@ -25,34 +25,27 @@ #include "net/buysellhandler.h" +#include "net/ea/buysellhandler.h" + #include "being.h" #include "net/net.h" #include "net/tmwa/messagehandler.h" -class BuyDialog; - namespace TmwAthena { -class BuySellHandler : public MessageHandler, public Net::BuySellHandler +class BuySellHandler : public MessageHandler, public Ea::BuySellHandler { public: BuySellHandler(); virtual void handleMessage(Net::MessageIn &msg); - virtual void requestSellList(std::string nick); - virtual void requestBuyList(std::string nick); - virtual void sendBuyRequest(std::string nick, ShopItem* item, - int amount); - virtual void sendSellRequest(std::string nick, ShopItem* item, - int amount); + virtual void processNpcBuy(Net::MessageIn &msg); - private: - int mNpcId; - BuyDialog *mBuyDialog; + virtual void processNpcSellResponse(Net::MessageIn &msg); }; } // namespace TmwAthena -- cgit v1.2.3-60-g2f50