* implemented emotes support & display
-> requires emote.scp extracted from client * updated: show incoming opcodes _before_ parsing them * updated DefScript: "emote" command will now also accept emote names (defined in emote.scp, field "name=...") * updated: replace %1$s in emote texts extracted from (non-english) dbc files with %s, this simplifies emote text displaying. * misc stuff
This commit is contained in:
parent
5731cfa03b
commit
c2938bf222
@ -110,7 +110,25 @@ DefReturnResult DefScriptPackage::SCemote(CmdSet& Set){
|
||||
logerror("Invalid Script call: SCEmote: WorldSession not valid");
|
||||
DEF_RETURN_ERROR;
|
||||
}
|
||||
uint32 id=atoi(Set.defaultarg.c_str());
|
||||
|
||||
// check if the given name exists in the database, if it does, use its record id; if not, convert emote name into a number.
|
||||
// this supports calls like "emote 126" and "emote ready"
|
||||
uint32 id = uint32(-1);
|
||||
SCPDatabaseMgr& dbmgr = ((PseuInstance*)parentMethod)->dbmgr;
|
||||
if(dbmgr.HasDB("emote"))
|
||||
{
|
||||
SCPDatabase& db = dbmgr.GetDB("emote");
|
||||
id = db.GetFieldByValue("name",DefScriptTools::stringToUpper(Set.defaultarg)); // emote names are always uppercased
|
||||
}
|
||||
if(id == uint32(-1))
|
||||
{
|
||||
id=atoi(Set.defaultarg.c_str());
|
||||
if(!id)
|
||||
{
|
||||
logerror("SCEmote: Invalid emote!");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
((PseuInstance*)parentMethod)->GetWSession()->SendEmote(id);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -62,14 +62,21 @@ bool SCPDatabase::HasField(uint32 id)
|
||||
bool SCPField::HasEntry(std::string e)
|
||||
{
|
||||
for(SCPEntryMap::iterator i = _map.begin(); i != _map.end(); i++)
|
||||
if(i->first == e)
|
||||
{
|
||||
std::string ch = i->first;
|
||||
if(ch == e)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string SCPField::GetString(std::string entry)
|
||||
{
|
||||
return HasEntry(entry) ? _map[entry] : "";
|
||||
//return HasEntry(entry) ? _map[entry] : "";
|
||||
if(HasEntry(entry))
|
||||
return _map[entry];
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
// note that this can take a while depending on the size of the database!
|
||||
@ -78,7 +85,7 @@ uint32 SCPDatabase::GetFieldByValue(std::string entry, std::string value)
|
||||
for(SCPFieldMap::iterator fm = _map.begin(); fm != _map.end(); fm++)
|
||||
if(fm->second.HasEntry(entry) && fm->second.GetString(entry)==value)
|
||||
return fm->first;
|
||||
return 0;
|
||||
return uint32(-1);
|
||||
}
|
||||
|
||||
bool SCPDatabaseMgr::HasDB(std::string n)
|
||||
|
||||
@ -6,7 +6,8 @@
|
||||
#include "WorldSession.h"
|
||||
#include "Channel.h"
|
||||
|
||||
void WorldSession::SendChatMessage(uint32 type, uint32 lang, std::string msg, std::string to){
|
||||
void WorldSession::SendChatMessage(uint32 type, uint32 lang, std::string msg, std::string to)
|
||||
{
|
||||
if((!_logged) || msg.empty())
|
||||
return;
|
||||
WorldPacket packet;
|
||||
@ -34,7 +35,8 @@ void WorldSession::SendChatMessage(uint32 type, uint32 lang, std::string msg, st
|
||||
SendWorldPacket(packet);
|
||||
}
|
||||
|
||||
void WorldSession::SendQueryPlayerName(uint64 guid){
|
||||
void WorldSession::SendQueryPlayerName(uint64 guid)
|
||||
{
|
||||
if((!_logged) || guid==0)
|
||||
return;
|
||||
WorldPacket packet;
|
||||
@ -44,7 +46,8 @@ void WorldSession::SendQueryPlayerName(uint64 guid){
|
||||
// to prevent opcode spam, we need to make a list with already requested names
|
||||
}
|
||||
|
||||
void WorldSession::SendPing(uint32 ping){
|
||||
void WorldSession::SendPing(uint32 ping)
|
||||
{
|
||||
if(!_logged)
|
||||
return;
|
||||
WorldPacket packet;
|
||||
@ -53,11 +56,13 @@ void WorldSession::SendPing(uint32 ping){
|
||||
SendWorldPacket(packet);
|
||||
}
|
||||
|
||||
void WorldSession::SendEmote(uint32 id){
|
||||
void WorldSession::SendEmote(uint32 id)
|
||||
{
|
||||
if(!_logged)
|
||||
return;
|
||||
WorldPacket packet;
|
||||
packet << id << id << GetMyChar()->GetTarget();
|
||||
int32 variation = 0; // randomized usually
|
||||
packet << id << (uint32)variation << GetMyChar()->GetTarget();
|
||||
packet.SetOpcode(CMSG_TEXT_EMOTE);
|
||||
SendWorldPacket(packet);
|
||||
}
|
||||
|
||||
@ -20,7 +20,16 @@ bool PlayerNameCache::AddInfo(uint64 guid, std::string name){
|
||||
return AddInfo(cacheItem);
|
||||
}
|
||||
|
||||
bool PlayerNameCache::AddInfo(PlayerNameCacheItem* cacheItem){
|
||||
bool PlayerNameCache::IsKnown(uint64 guid)
|
||||
{
|
||||
for(std::vector<PlayerNameCacheItem*>::iterator i=_cache.begin(); i!=_cache.end(); i++)
|
||||
if(guid==(*i)->_guid)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PlayerNameCache::AddInfo(PlayerNameCacheItem* cacheItem)
|
||||
{
|
||||
for(std::vector<PlayerNameCacheItem*>::iterator i=_cache.begin(); i!=_cache.end(); i++)
|
||||
if(cacheItem->_guid==(*i)->_guid)
|
||||
return false;
|
||||
@ -28,21 +37,24 @@ bool PlayerNameCache::AddInfo(PlayerNameCacheItem* cacheItem){
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string PlayerNameCache::GetName(uint64 guid){
|
||||
std::string PlayerNameCache::GetName(uint64 guid)
|
||||
{
|
||||
for(std::vector<PlayerNameCacheItem*>::iterator i=_cache.begin(); i!=_cache.end(); i++)
|
||||
if(guid==(*i)->_guid)
|
||||
return (*i)->_name;
|
||||
return "";
|
||||
}
|
||||
|
||||
uint64 PlayerNameCache::GetGuid(std::string name){
|
||||
uint64 PlayerNameCache::GetGuid(std::string name)
|
||||
{
|
||||
for(std::vector<PlayerNameCacheItem*>::iterator i=_cache.begin(); i!=_cache.end(); i++)
|
||||
if(name==(*i)->_name)
|
||||
return (*i)->_guid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool PlayerNameCache::SaveToFile(void){
|
||||
bool PlayerNameCache::SaveToFile(void)
|
||||
{
|
||||
log("Saving PlayerNameCache...");
|
||||
char *fn="./cache/playernames.cache";
|
||||
std::fstream fh;
|
||||
@ -71,7 +83,8 @@ bool PlayerNameCache::SaveToFile(void){
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PlayerNameCache::ReadFromFile(void){
|
||||
bool PlayerNameCache::ReadFromFile(void)
|
||||
{
|
||||
char *fn="./cache/playernames.cache";
|
||||
log("Loading PlayerNameCache...");
|
||||
bool success=true;
|
||||
@ -115,7 +128,8 @@ bool PlayerNameCache::ReadFromFile(void){
|
||||
return success;
|
||||
}
|
||||
|
||||
uint32 PlayerNameCache::GetSize(void){
|
||||
uint32 PlayerNameCache::GetSize(void)
|
||||
{
|
||||
return _cache.size();
|
||||
}
|
||||
|
||||
|
||||
@ -9,6 +9,7 @@ struct PlayerNameCacheItem {
|
||||
class PlayerNameCache {
|
||||
public:
|
||||
std::string GetName(uint64);
|
||||
bool IsKnown(uint64);
|
||||
uint64 GetGuid(std::string);
|
||||
bool AddInfo(uint64 guid, std::string name);
|
||||
bool AddInfo(PlayerNameCacheItem*);
|
||||
|
||||
@ -103,25 +103,17 @@ void WorldSession::Update(void)
|
||||
|
||||
|
||||
OpcodeHandler *table = _GetOpcodeHandlerTable();
|
||||
|
||||
uint16 hpos;
|
||||
bool known=false;
|
||||
while(pktQueue.size())
|
||||
{
|
||||
WorldPacket *packet = pktQueue.next();
|
||||
|
||||
for (uint16 i = 0; table[i].handler != NULL; i++)
|
||||
for (hpos = 0; table[hpos].handler != NULL; hpos++)
|
||||
{
|
||||
if (table[i].opcode == packet->GetOpcode())
|
||||
if (table[hpos].opcode == packet->GetOpcode())
|
||||
{
|
||||
try
|
||||
{
|
||||
(this->*table[i].handler)(*packet);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
logerror("Exception while handling opcode %u!",packet->GetOpcode());
|
||||
logerror("Data: pktsize=%u, handler=0x%X queuesize=%u",packet->size(),table[i].handler,pktQueue.size());
|
||||
}
|
||||
|
||||
known=true;
|
||||
break;
|
||||
}
|
||||
@ -140,8 +132,22 @@ void WorldSession::Update(void)
|
||||
|| (GetInstance()->GetConf()->showopcodes==3) )
|
||||
{
|
||||
if(!(GetInstance()->GetConf()->hidefreqopcodes && hideOpcode))
|
||||
logcustom(1,YELLOW,">> Opcode %u [%s] (%s)", packet->GetOpcode(), GetOpcodeName(packet->GetOpcode()), known ? "Known" : "UNKNOWN");
|
||||
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;
|
||||
}
|
||||
@ -194,6 +200,8 @@ OpcodeHandler *WorldSession::_GetOpcodeHandlerTable() const
|
||||
{SMSG_LEARNED_SPELL, &WorldSession::_HandleLearnedSpellOpcode},
|
||||
{SMSG_REMOVED_SPELL, &WorldSession::_HandleLearnedSpellOpcode},
|
||||
{SMSG_CHANNEL_LIST, &WorldSession::_HandleChannelListOpcode},
|
||||
{SMSG_EMOTE, &WorldSession::_HandleEmoteOpcode},
|
||||
{SMSG_TEXT_EMOTE, &WorldSession::_HandleTextEmoteOpcode},
|
||||
|
||||
// table termination
|
||||
{ 0, NULL }
|
||||
@ -687,3 +695,123 @@ 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -85,6 +85,8 @@ private:
|
||||
void _HandleLearnedSpellOpcode(WorldPacket& recvPacket);
|
||||
void _HandleRemovedSpellOpcode(WorldPacket& recvPacket);
|
||||
void _HandleChannelListOpcode(WorldPacket& recvPacket);
|
||||
void _HandleEmoteOpcode(WorldPacket& recvPacket);
|
||||
void _HandleTextEmoteOpcode(WorldPacket& recvPacket);
|
||||
|
||||
void _MovementUpdate(uint8 objtypeid, uint64 guid, WorldPacket& recvPacket); // Helper for _HandleUpdateObjectOpcode
|
||||
void _ValuesUpdate(uint64 uguid, WorldPacket& recvPacket); // ...
|
||||
|
||||
@ -132,7 +132,19 @@ bool ConvertDBC(void)
|
||||
{
|
||||
if((*ix).getInt(stringpos)) // find out which field is used, 0 if not used
|
||||
{
|
||||
EmoteDataStorage[em].push_back( fname + "=" + (*ix).getString(stringpos) );
|
||||
std::string tx = (*ix).getString(stringpos);
|
||||
uint32 fpos=0;
|
||||
// the following block replaces %x$s (where x is a number) with %s
|
||||
do
|
||||
{
|
||||
if(fpos+4 < tx.length() && tx[fpos]=='%' && tx[fpos+2]=='$' && tx[fpos+3]=='s' && isdigit(tx[fpos+1]))
|
||||
{
|
||||
tx.erase(fpos,4);
|
||||
tx.insert(fpos,"%s");
|
||||
}
|
||||
fpos++;
|
||||
} while(fpos < tx.length());
|
||||
EmoteDataStorage[em].push_back( fname + "=" + tx );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user