#include "common.h" #include "Auth/Sha1.h" #include "Auth/BigNumber.h" #include "Auth/AuthCrypt.h" #include "Opcodes.h" #include "WorldPacket.h" #include "WorldSocket.h" #include "RealmSocket.h" #include "Channel.h" #include "RealmSession.h" #include "WorldSession.h" struct OpcodeHandler { uint16 opcode; void (WorldSession::*handler)(WorldPacket& recvPacket); }; WorldSession::WorldSession(PseuInstance *in) { logdebug("-> Starting WorldSession from instance 0x%X",in); // should never output a null ptr _instance = in; _mustdie=false; _logged=false; _socket=NULL; _myGUID=0; // i dont have a guid yet _channels = new Channel(this); _sh.SetAutoCloseSockets(false); //... } WorldSession::~WorldSession() { WorldPacket *packet; // clear the queue while(pktQueue.size()) { packet = pktQueue.next(); delete packet; } if(_channels) delete _channels; if(_socket) delete _socket; } void WorldSession::Start(void) { log("Connecting to '%s' on port %u",GetInstance()->GetConf()->worldhost.c_str(),GetInstance()->GetConf()->worldport); _socket=new WorldSocket(_sh,this); _socket->Open(GetInstance()->GetConf()->worldhost,GetInstance()->GetConf()->worldport); if(GetInstance()->GetRSession()) { GetInstance()->GetRSession()->SetMustDie(); // realm session is no longer needed } _sh.Add(_socket); // if we cant connect, wait until the socket gives up (after 5 secs) while( (!MustDie()) && (!_socket->IsOk()) && (!GetInstance()->Stopped()) ) { _sh.Select(3,0); GetInstance()->Sleep(100); } } void WorldSession::_LoadCache(void) { logdetail("Loading Cache..."); plrNameCache.ReadFromFile(); // load names/guids of known players ItemProtoCache_InsertDataToSession(this); //... } void WorldSession::AddToPktQueue(WorldPacket *pkt) { pktQueue.add(pkt); } void WorldSession::SendWorldPacket(WorldPacket &pkt) { if(GetInstance()->GetConf()->showmyopcodes) logcustom(0,BROWN,"<< Opcode %u [%s]", pkt.GetOpcode(), GetOpcodeName(pkt.GetOpcode())); if(_socket && _socket->IsOk()) _socket->SendWorldPacket(pkt); else { logerror("WorldSession: Can't send WorldPacket, socket doesn't exist or is not ready."); } } void WorldSession::Update(void) { if( _sh.GetCount() ) // the socket will remove itself from the handler if it got closed _sh.Select(0,0); else // so we just need to check if the socket doesnt exist or if it exists but isnt valid anymore. { // if thats the case, we dont need the session anymore either if(!_socket || (_socket && !_socket->IsOk())) { _OnLeaveWorld(); SetMustDie(); } } OpcodeHandler *table = _GetOpcodeHandlerTable(); uint16 hpos; bool known=false; while(pktQueue.size()) { WorldPacket *packet = pktQueue.next(); for (hpos = 0; table[hpos].handler != NULL; hpos++) { if (table[hpos].opcode == packet->GetOpcode()) { known=true; break; } } bool hideOpcode = false; // TODO: Maybe make table or something with all the frequently opcodes if (packet->GetOpcode() == SMSG_MONSTER_MOVE) { hideOpcode = true; } if( (known && GetInstance()->GetConf()->showopcodes==1) || ((!known) && GetInstance()->GetConf()->showopcodes==2) || (GetInstance()->GetConf()->showopcodes==3) ) { if(!(GetInstance()->GetConf()->hidefreqopcodes && hideOpcode)) logcustom(1,YELLOW,">> Opcode %u [%s] (%s, %u bytes)", packet->GetOpcode(), GetOpcodeName(packet->GetOpcode()), known ? "Known" : "UNKNOWN", packet->size()); } if(known) { try { (this->*table[hpos].handler)(*packet); } catch (...) { logerror("Exception while handling opcode %u!",packet->GetOpcode()); logerror("Data: pktsize=%u, handler=0x%X queuesize=%u",packet->size(),table[hpos].handler,pktQueue.size()); } } delete packet; known=false; } _DoTimedActions(); } OpcodeHandler *WorldSession::_GetOpcodeHandlerTable() const { static OpcodeHandler table[] = { { SMSG_AUTH_CHALLENGE, &WorldSession::_HandleAuthChallengeOpcode }, { SMSG_AUTH_RESPONSE, &WorldSession::_HandleAuthResponseOpcode }, {SMSG_CHAR_ENUM, &WorldSession::_HandleCharEnumOpcode}, {SMSG_SET_PROFICIENCY, &WorldSession::_HandleSetProficiencyOpcode}, {SMSG_ACCOUNT_DATA_MD5, &WorldSession::_HandleAccountDataMD5Opcode}, {SMSG_MESSAGECHAT, &WorldSession::_HandleMessageChatOpcode}, {SMSG_NAME_QUERY_RESPONSE, &WorldSession::_HandleNameQueryResponseOpcode}, {SMSG_PONG, &WorldSession::_HandlePongOpcode}, {SMSG_TRADE_STATUS, &WorldSession::_HandleTradeStatusOpcode}, {SMSG_GROUP_INVITE, &WorldSession::_HandleGroupInviteOpcode}, {SMSG_CHANNEL_NOTIFY, &WorldSession::_HandleChannelNotifyOpcode}, // movement opcodes {MSG_MOVE_SET_FACING, &WorldSession::_HandleMovementOpcode}, {MSG_MOVE_START_FORWARD, &WorldSession::_HandleMovementOpcode}, {MSG_MOVE_START_BACKWARD, &WorldSession::_HandleMovementOpcode}, {MSG_MOVE_STOP, &WorldSession::_HandleMovementOpcode}, {MSG_MOVE_START_STRAFE_LEFT, &WorldSession::_HandleMovementOpcode}, {MSG_MOVE_START_STRAFE_RIGHT, &WorldSession::_HandleMovementOpcode}, {MSG_MOVE_STOP_STRAFE, &WorldSession::_HandleMovementOpcode}, {MSG_MOVE_JUMP, &WorldSession::_HandleMovementOpcode}, {MSG_MOVE_START_TURN_LEFT, &WorldSession::_HandleMovementOpcode}, {MSG_MOVE_START_TURN_RIGHT, &WorldSession::_HandleMovementOpcode}, {MSG_MOVE_STOP_TURN, &WorldSession::_HandleMovementOpcode}, {MSG_MOVE_START_SWIM, &WorldSession::_HandleMovementOpcode}, {MSG_MOVE_STOP_SWIM, &WorldSession::_HandleMovementOpcode}, {MSG_MOVE_HEARTBEAT, &WorldSession::_HandleMovementOpcode}, {MSG_MOVE_FALL_LAND, &WorldSession::_HandleMovementOpcode}, {MSG_MOVE_TELEPORT_ACK, &WorldSession::_HandleTelePortAckOpcode}, {SMSG_COMPRESSED_UPDATE_OBJECT, &WorldSession::_HandleCompressedUpdateObjectOpcode}, {SMSG_UPDATE_OBJECT, &WorldSession::_HandleUpdateObjectOpcode}, {SMSG_CAST_RESULT, &WorldSession::_HandleCastResultOpcode}, {SMSG_ITEM_QUERY_SINGLE_RESPONSE, &WorldSession::_HandleItemQuerySingleResponseOpcode}, {SMSG_DESTROY_OBJECT, &WorldSession::_HandleDestroyObjectOpcode}, {SMSG_INITIAL_SPELLS, &WorldSession::_HandleInitialSpellsOpcode}, {SMSG_LEARNED_SPELL, &WorldSession::_HandleLearnedSpellOpcode}, {SMSG_REMOVED_SPELL, &WorldSession::_HandleLearnedSpellOpcode}, {SMSG_CHANNEL_LIST, &WorldSession::_HandleChannelListOpcode}, {SMSG_EMOTE, &WorldSession::_HandleEmoteOpcode}, {SMSG_TEXT_EMOTE, &WorldSession::_HandleTextEmoteOpcode}, {SMSG_NEW_WORLD, &WorldSession::_HandleNewWorldOpcode}, // table termination { 0, NULL } }; return table; } void WorldSession::SetTarget(uint64 guid) { SendSetSelection(guid); } void WorldSession::_OnEnterWorld(void) { if(!InWorld()) { _logged=true; GetInstance()->GetScripts()->variables.Set("@inworld","true"); GetInstance()->GetScripts()->RunScript("_enterworld",NULL); } } void WorldSession::_OnLeaveWorld(void) { if(InWorld()) { _logged=false; GetInstance()->GetScripts()->RunScript("_leaveworld",NULL); GetInstance()->GetScripts()->variables.Set("@inworld","false"); } } void WorldSession::_DoTimedActions(void) { static clock_t pingtime=0; if(InWorld()) { if(pingtime < clock()) { pingtime=clock() + 30*CLOCKS_PER_SEC; SendPing(clock()); } //... } } /////////////////////////////////////////////////////////////// // Opcode Handlers /////////////////////////////////////////////////////////////// void WorldSession::_HandleAuthChallengeOpcode(WorldPacket& recvPacket) { std::string acc = stringToUpper(GetInstance()->GetConf()->accname); uint32 serverseed; recvPacket >> serverseed; logdebug("Auth: serverseed=0x%X",serverseed); Sha1Hash digest; digest.UpdateData(acc); uint32 unk=0; digest.UpdateData((uint8*)&unk,sizeof(uint32)); BigNumber clientseed; clientseed.SetRand(8*4); uint32 clientseed_uint32=clientseed.AsDword(); digest.UpdateData((uint8*)&clientseed_uint32,sizeof(uint32)); digest.UpdateData((uint8*)&serverseed,sizeof(uint32)); digest.UpdateBigNumbers(&(GetInstance()->GetSessionKey()),NULL); digest.Finalize(); WorldPacket auth; auth<<(uint32)(GetInstance()->GetConf()->clientbuild)<InitCrypt(GetInstance()->GetSessionKey().AsByteArray(), 40); } void WorldSession::_HandleAuthResponseOpcode(WorldPacket& recvPacket) { uint8 errcode; recvPacket >> errcode; if(errcode==0xC){ logdetail("World Authentication successful, preparing for char list request..."); WorldPacket pkt; pkt.SetOpcode(CMSG_CHAR_ENUM); SendWorldPacket(pkt); } else { logcritical("World Authentication failed, errcode=0x%X",(unsigned char)errcode); GetInstance()->SetError(); } } void WorldSession::_HandleCharEnumOpcode(WorldPacket& recvPacket) { uint8 num; PlayerEnum plr[10]; // max characters per realm is 10 uint8 dummy8; recvPacket >> num; if(num==0){ logerror("No chars found!"); GetInstance()->SetError(); return; } _LoadCache(); // we are about to login, so we need cache data logdetail("W: Chars in list: %u\n",num); for(unsigned int i=0;i> plr[i]._guid; recvPacket >> plr[i]._name; recvPacket >> plr[i]._race; recvPacket >> plr[i]._class; recvPacket >> plr[i]._gender; recvPacket >> plr[i]._bytes1; recvPacket >> plr[i]._bytes2; recvPacket >> plr[i]._bytes3; recvPacket >> plr[i]._bytes4; recvPacket >> plr[i]._bytesx; recvPacket >> plr[i]._level; recvPacket >> plr[i]._zoneId; recvPacket >> plr[i]._mapId; recvPacket >> plr[i]._x; recvPacket >> plr[i]._y; recvPacket >> plr[i]._z; recvPacket >> plr[i]._guildId; recvPacket >> dummy8; recvPacket >> plr[i]._flags; recvPacket >> dummy8 >> dummy8 >> dummy8; recvPacket >> plr[i]._petInfoId; recvPacket >> plr[i]._petLevel; recvPacket >> plr[i]._petFamilyId; for(unsigned int inv=0;inv<20;inv++) { recvPacket >> plr[i]._items[inv].displayId >> plr[i]._items[inv].inventorytype; } plrNameCache.AddInfo(plr[i]._guid, plr[i]._name); } bool char_found=false; int playerNum = 0; for(unsigned int i=0;i coords: map=%u zone=%u x=%f y=%f z=%f", plr[i]._mapId,plr[i]._zoneId,plr[i]._x,plr[i]._y,plr[i]._z); for(unsigned int inv=0;inv<20;inv++) { if(plr[i]._items[inv].displayId) logdebug("-> Has Item: Model=%u InventoryType=%u",plr[i]._items[inv].displayId,plr[i]._items[inv].inventorytype); } if(plr[i]._name==GetInstance()->GetConf()->charname) { char_found=true; _myGUID=plr[i]._guid; GetInstance()->GetScripts()->variables.Set("@myrace",toString(plr[i]._race)); playerNum = i; } } if(!char_found){ logerror("Character \"%s\" was not found on char list!",GetInstance()->GetConf()->charname.c_str()); GetInstance()->SetError(); return; } else { log("Entering World with Character \"%s\"...",GetInstance()->GetConf()->charname.c_str()); // create the character and add it to the objmgr. MyCharacter *my = new MyCharacter(); my->Create(_myGUID); objmgr.Add(my); WorldPacket pkt; pkt.SetOpcode(CMSG_PLAYER_LOGIN); pkt << _myGUID; SendWorldPacket(pkt); } } void WorldSession::_HandleSetProficiencyOpcode(WorldPacket& recvPacket) { _OnEnterWorld(); } void WorldSession::_HandleAccountDataMD5Opcode(WorldPacket& recvPacket) { _OnEnterWorld(); } void WorldSession::_HandleMessageChatOpcode(WorldPacket& recvPacket) { uint8 type=0; uint32 lang=0; uint64 target_guid=0; uint32 msglen=0; std::string msg,channel=""; bool isCmd=false; recvPacket >> type >> lang; std::string langname = GetDBMgr().GetLangName(lang); const char* ln = langname.c_str(); if (type == CHAT_MSG_CHANNEL) { recvPacket >> channel; // extract channel name } recvPacket >> target_guid; std::string plrname; if(target_guid){ plrname=plrNameCache.GetName(target_guid); if(plrname.empty()) { SendQueryPlayerName(target_guid); plrname="Unknown Entity"; } } GetInstance()->GetScripts()->variables.Set("@thismsg_name",plrname); GetInstance()->GetScripts()->variables.Set("@thismsg",toString(target_guid)); if(type == CHAT_MSG_SAY || type == CHAT_MSG_YELL || type == CHAT_MSG_PARTY) recvPacket >> target_guid; recvPacket >> msglen >> msg; if (type == CHAT_MSG_SYSTEM) { logcustom(0,WHITE,"SYSMSG: \"%s\"",msg.c_str()); } else if (type==CHAT_MSG_WHISPER ) { logcustom(0,WHITE,"WHISP: %s [%s]: %s",plrname.c_str(),ln,msg.c_str()); } else if (type==CHAT_MSG_CHANNEL ) { logcustom(0,WHITE,"CHANNEL: [%s]: %s [%s]: %s",channel.c_str(),plrname.c_str(),ln,msg.c_str()); } else if (type==CHAT_MSG_SAY ) { logcustom(0,WHITE,"CHAT: %s [%s]: %s",plrname.c_str(),ln,msg.c_str()); } else if (type==CHAT_MSG_YELL ) { logcustom(0,WHITE,"CHAT: %s yells [%s]: %s ",plrname.c_str(),ln,msg.c_str()); } else if (type==CHAT_MSG_WHISPER_INFORM ) { logcustom(0,WHITE,"TO %s [%s]: %s",plrname.c_str(),ln,msg.c_str()); } else if (type==CHAT_MSG_GUILD ) { logcustom(0,WHITE,"GUILD: %s [%s]: %s",plrname.c_str(),ln,msg.c_str()); } else if (type==CHAT_MSG_PARTY ) { logcustom(0,WHITE,"PARTY: %s [%s]: %s",plrname.c_str(),ln,msg.c_str()); } else if (type==CHAT_MSG_EMOTE ) { logcustom(0,WHITE,"EMOTE: %s [%s]: %s",plrname.c_str(),ln,msg.c_str()); } else { logcustom(0,WHITE,"UNK CHAT TYPE (%u): %s [%s]: %s",type,plrname.c_str(),ln,msg.c_str()); } if(target_guid!=GetGuid() && msg.length()>1 && msg.at(0)=='-' && GetInstance()->GetConf()->allowgamecmd) isCmd=true; // some fun code :P if(type==CHAT_MSG_SAY && target_guid!=_myGUID && !isCmd) { // TODO: insert a good ChatAI here. if (GetInstance()->GetConf()->enablechatai) { if(msg=="lol") SendChatMessage(CHAT_MSG_SAY,lang,"say \"lol\" if you have nothing else to say... lol xD",""); else if(msg.length()>4 && msg.find("you?")!=std::string::npos) SendChatMessage(CHAT_MSG_SAY,lang,GetInstance()->GetScripts()->variables.Get("@version").append(" -- i am a bot, made by False.Genesis, my master."),""); else if(msg=="hi") SendChatMessage(CHAT_MSG_SAY,lang,"Hi, wadup?",""); else if(msg.length()<12 && msg.find("wtf")!=std::string::npos) SendChatMessage(CHAT_MSG_SAY,lang,"Yeah, WTF is a good way to say you dont understand anything... :P",""); else if(msg.length()<15 && (msg.find("omg")!=std::string::npos || msg.find("omfg")!=std::string::npos) ) SendChatMessage(CHAT_MSG_SAY,lang,"OMG a bot logged in, you don't believe it :O",""); else if(msg.find("from")!=std::string::npos || msg.find("download")!=std::string::npos) SendChatMessage(CHAT_MSG_SAY,lang,"http://my.opera.com/PseuWoW",""); else if(msg.find("Genesis")!=std::string::npos || msg.find("genesis")!=std::string::npos) SendChatMessage(CHAT_MSG_YELL,lang,"False.Genesis, they are calling you!! Come here, master xD",""); } } if(isCmd) { GetInstance()->GetScripts()->variables.Set("@thiscmd_name",plrname); GetInstance()->GetScripts()->variables.Set("@thiscmd",toString(target_guid)); std::string lin=msg.substr(1,msg.length()-1); try { GetInstance()->GetScripts()->My_Run(lin,plrname); } catch (...) { SendChatMessage(CHAT_MSG_SAY,0,"Exception while trying to execute: [ "+lin+" ]",""); } } if(type==CHAT_MSG_WHISPER && (!isCmd) && target_guid!=GetGuid()) { GetInstance()->GetScripts()->variables.Set("@thiswhisper_name",plrname); GetInstance()->GetScripts()->variables.Set("@thiswhisper",toString(target_guid)); GetInstance()->GetScripts()->variables.Set("@thiswhisper_lang",toString((uint64)lang)); GetInstance()->GetScripts()->RunScript("_onwhisper",NULL); } // the following block searches for items in chat and queries them if they are unknown if(!isCmd && target_guid!=_myGUID && msg.length()>strlen(CHAT_ITEM_BEGIN_STRING)) { for(uint32 pos=0;pos> pguid >> pname; if(pname.length()>12 || pname.length()<2) return; // playernames maxlen=12, minlen=2 // rest of the packet is not interesting for now if(plrNameCache.AddInfo(pguid,pname)) { logdetail("CACHE: Assigned new player name: '%s' = " I64FMTD ,pname.c_str(),pguid); if(GetInstance()->GetConf()->debug > 1) SendChatMessage(CHAT_MSG_SAY,0,"Player "+pname+" added to cache.",""); } WorldObject *wo = (WorldObject*)objmgr.GetObj(pguid); if(wo) wo->SetName(pname); } void WorldSession::_HandlePongOpcode(WorldPacket& recvPacket) { uint32 pong; recvPacket >> pong; if(GetInstance()->GetConf()->notifyping) log("Recieved Ping reply: %u ms latency.",clock()-pong); } void WorldSession::_HandleTradeStatusOpcode(WorldPacket& recvPacket) { recvPacket.hexlike(); uint8 unk; recvPacket >> unk; if(unk==1) { SendChatMessage(CHAT_MSG_SAY,0,"It has no sense trying to trade with me, that feature is not yet implemented!",""); WorldPacket pkt; pkt.SetOpcode(CMSG_CANCEL_TRADE); SendWorldPacket(pkt); } } void WorldSession::_HandleGroupInviteOpcode(WorldPacket& recvPacket) { recvPacket.hexlike(); WorldPacket pkt; pkt.SetOpcode(CMSG_GROUP_DECLINE); SendWorldPacket(pkt); } void WorldSession::_HandleMovementOpcode(WorldPacket& recvPacket) { uint32 flags, time; float x, y, z, o; uint64 guid; std::string plrname; guid = recvPacket.GetPackedGuid(); recvPacket >> flags >> time >> x >> y >> z >> o; /*if(guid){ plrname=plrNameCache.GetName(guid); if(plrname.empty()) { SendQueryPlayerName(guid); plrname="Unknown Entity"; } }*/ } void WorldSession::_HandleTelePortAckOpcode(WorldPacket& recvPacket) { uint32 unk32,time; uint64 guid; float x, y, z, o; guid = recvPacket.GetPackedGuid(); recvPacket >> unk32 >> unk32 >> time >> x >> y >> z >> o >> unk32; logdetail("Got teleported, data: x: %f, y: %f, z: %f, o: %f, guid: "I64FMT, x, y, z, o, guid); // TODO: put this into a capsule class later, that autodetects movement flags etc. WorldPacket response; response.SetOpcode(MSG_MOVE_FALL_LAND); response << uint32(0) << (uint32)getMSTime(); // no flags; time correct? response << x << y << z << o << uint32(0); SendWorldPacket(response); } void WorldSession::_HandleNewWorldOpcode(WorldPacket& recvPacket) { uint32 mapid; float x,y,z,o; // we assume we are NOT on a transport! // else we had to do the following before: // recvPacket >> tmapid >> tx >> ty >> tz >> to; recvPacket >> mapid >> x >> y >> z >> o; GetMyChar()->ClearSpells(); // will be resent by server // clear action buttons } void WorldSession::_HandleChannelNotifyOpcode(WorldPacket& recvPacket) { _channels->HandleNotifyOpcode(recvPacket); } void WorldSession::_HandleCastResultOpcode(WorldPacket& recvPacket) { uint32 spellid; uint8 flag,result; recvPacket >> spellid >> flag; if(flag) { recvPacket >> result; logdetail("Cast of spell %u failed. flag=%u, result=%u",spellid,flag,result); } else { logdetail("Cast of spell %u successful.",spellid); } } void WorldSession::_HandleInitialSpellsOpcode(WorldPacket& recvPacket) { uint8 unk; uint16 spellid,spellslot,count; recvPacket >> unk >> count; logdebug("Got initial spells list, %u spells.",count); for(uint16 i = 0; i < count; i++) { recvPacket >> spellid >> spellslot; logdebug("Initial Spell: id=%u slot=%u",spellid,spellslot); GetMyChar()->AddSpell(spellid, spellslot); } } void WorldSession::_HandleLearnedSpellOpcode(WorldPacket& recvPacket) { uint32 spellid; recvPacket >> spellid; GetMyChar()->AddSpell(spellid, 0); // other spells must be moved by +1 in slot? logdebug("Learned spell: id=%u",spellid); } void WorldSession::_HandleRemovedSpellOpcode(WorldPacket& recvPacket) { uint32 spellid; recvPacket >> spellid; GetMyChar()->RemoveSpell(spellid); logdebug("Unlearned spell: id=%u",spellid); } void WorldSession::_HandleChannelListOpcode(WorldPacket& recvPacket) { _channels->HandleListRequest(recvPacket); } void WorldSession::_HandleEmoteOpcode(WorldPacket& recvPacket) { std::string plrname; uint32 anim; // animation id? uint64 guid; // guid of the unit performing the emote recvPacket >> anim >> guid; // TODO: check if the emote came from a player or a mob, and query mob name if it was a mob if(guid) { plrname=plrNameCache.GetName(guid); if(plrname.empty()) { SendQueryPlayerName(guid); plrname="Unknown Entity"; } } // TODO: check for mobs logdebug(I64FMT " / %s performing emote; anim=%u",guid,plrname.c_str(),anim); // TODO: show emote in GUI :P } void WorldSession::_HandleTextEmoteOpcode(WorldPacket& recvPacket) { std::string name; // name of emote target std::string name_from; // name of the unit performing the emote uint32 emotetext; // emote id int32 emotev; // variation (like different texts on /flirt and so on) uint32 namelen; // length of name of emote target (without '\0' ) uint64 guid; // guid of the unit performing the emote uint8 c; // temp recvPacket >> guid >> emotetext >> *((uint32*)&emotev) >> namelen; // get the target name, which is NOT null-terminated for(uint32 i = 0; i < namelen; i++) { recvPacket >> c; if(c) name += c; } logdebug(I64FMT " Emote: name=%s text=%u variation=%i len=%u",guid,name.c_str(),emotetext,emotev,namelen); SCPDatabaseMgr& dbmgr = GetInstance()->dbmgr; if(dbmgr.HasDB("emote")) { SCPDatabase& db = dbmgr.GetDB("emote"); std::string target,target2; bool targeted=false; // if the emote is directed to anyone around or a specific target bool targeted_me=false; // if the emote was targeted to us if it was targeted bool from_me=false; // if we did the emote bool female=false; // if emote causer is female if(GetMyChar()->GetGUID() == guid) // we caused the emote from_me=true; if(name.length()) // is it directed to someone? { targeted=true; // if yes, we have a target if(GetMyChar()->GetName() == name) // if the name is ours, its directed to us targeted_me=true; } Unit *u = (Unit*)objmgr.GetObj(guid); if(u) { if(u->GetGender() != 0) // female female=true; name_from = u->GetName(); } // if we targeted ourself, the general emote is used! if(targeted && from_me && targeted_me) targeted_me=false; // now build the string that is used to lookup the text in the database if(from_me) target += "me"; else target += "one"; if(targeted) { target += "to"; if(targeted_me) target += "me"; else target += "one"; } else target += "general"; // not all emotes have a female version, so check if there is one in the database if(female && db.GetField(emotetext).HasEntry(target + "female")) target += "female"; logdebug("Looking up 'emote' SCP field %u entry '%s'",emotetext,target.c_str()); std::string etext; etext = db.GetField(emotetext).GetString(target); char out[300]; // should be enough if(from_me) sprintf(out,etext.c_str(),name.c_str()); else sprintf(out,etext.c_str(),name_from.c_str(),name.c_str()); logcustom(0,WHITE,"EMOTE: %s",out); } else { logerror("Can't display emote text %u, SCP database \"emote\" not loaded.",emotetext); } }