diff --git a/Source/Client/Session.h b/Source/Client/Session.h index e132aaeb..2d7c2f40 100644 --- a/Source/Client/Session.h +++ b/Source/Client/Session.h @@ -341,6 +341,8 @@ public: CSkillInfo HomunSkillAt(int index) const; int CountProhibitionLog(std::uint32_t aid); void DeleteProhibitionLog(std::uint32_t aid, std::uint32_t minutes, WindowDialogs& dialogs); + void AddProhibitionLog(std::uint32_t aid, std::uint32_t minutes); + int ProcessEmotionShortcut(const char* text); int IsAdminSubMode(int subMode); void GetMonsterSkillInfo(const char* monster, int skill, int* action, int* loopType, WindowDialogs& dialogs); const char* DecodePlayerName() const; diff --git a/Source/Contents/CashEmotionMgr.cpp b/Source/Contents/CashEmotionMgr.cpp index d3969e80..c548eb03 100644 --- a/Source/Contents/CashEmotionMgr.cpp +++ b/Source/Contents/CashEmotionMgr.cpp @@ -30,7 +30,7 @@ ChatSuggestions* g_chatSuggestions = nullptr; class ICashEmotionAction { public: virtual ~ICashEmotionAction(); - virtual void Execute(int packId, int argument) = 0; + virtual bool Execute(int packId, int argument) = 0; }; class UICashEmotionPack_Purchase : public UICashEmotionPack { @@ -662,6 +662,20 @@ void CCashEmotionMgr::AddShortcut(const std::string& name, int packId, int emoti } } +// OFFSET: 0x0059C4F0 +// FUNCTION: fn_02976 +bool CCashEmotionMgr::PlaySearchedEmotion(const std::string& name) { + if (this->m_action) { + auto found = this->m_searchIndex.find(name); + const std::pair* emotion = found == this->m_searchIndex.end() ? nullptr : &found->second; + if (emotion) { + return this->m_action->Execute(emotion->first, emotion->second); + } + } + + return false; +} + // OFFSET: 0x0059C0A0 // FUNCTION: fn_02972 void CCashEmotionMgr::SetOwnedPack(int packId, unsigned int expiry) { diff --git a/Source/Contents/CashEmotionMgr.h b/Source/Contents/CashEmotionMgr.h index 5543943e..a687bf27 100644 --- a/Source/Contents/CashEmotionMgr.h +++ b/Source/Contents/CashEmotionMgr.h @@ -12,6 +12,7 @@ #include class CCashEmotionMgr; +class ICashEmotionAction; struct CashEmotionKeyHash { // INLINED @@ -139,6 +140,7 @@ public: int LookupSpecialEmotion(CashEmotionSpecial type); int LookupSpecialEmotion(CashEmotionSpecial type, const char* name); void AddShortcut(const std::string& name, int packId, int emotionId); + bool PlaySearchedEmotion(const std::string& name); bool IsChatProhibitEmotion(int emotionId); bool IsPackAvailable(unsigned int now, int packId); bool IsOwnedPack(int packId); @@ -150,7 +152,7 @@ public: protected: /* 0004 */ bool m_enabled; /* 0005 */ std::uint8_t unknown_0005[3]; - /* 0008 */ void* m_action; + /* 0008 */ ICashEmotionAction* m_action; /* 000C */ int unknown_000C; /* 0010 */ std::unordered_map unknown_0010; /* 0030 */ std::unordered_map m_emotionsByPack; diff --git a/Source/Contents/Skills/ClientSkills.cpp b/Source/Contents/Skills/ClientSkills.cpp index d883f72c..568138f9 100644 --- a/Source/Contents/Skills/ClientSkills.cpp +++ b/Source/Contents/Skills/ClientSkills.cpp @@ -3,17 +3,21 @@ #include "Client/Mode/CGameMode.h" #include "Client/Session.h" #include "Contents/Actors/RenderObject.h" +#include "Contents/CashEmotionMgr.h" #include "Contents/ClientState.h" #include "Contents/Jobs/JobId.h" #include "Contents/Jobs/JobInheritance.h" #include "Contents/UserData/CharacterConfigSave.h" #include "Contents/World/World.h" +#include "File/File.h" +#include "Network/TextEncoding.h" #include "Script/LuaScript.h" #include "UI/Shortcuts/ShortcutBindings.h" #include "UI/Window/UIWindow.h" #include "UI/Window/UIWindowMgr.h" #include "UI/Window/WindowDialogs.h" #include "UI/Window/WindowManagerWindows.h" +#include "Utility/Tokenizer.h" #include #include @@ -738,3 +742,88 @@ CRenderObject* CSession::FindGameActor(std::uint32_t aid) const { return mode->scene->GetGameActorByAID(aid); } + +// OFFSET: 0x00E07AA0 +// FUNCTION: fn_23749 +void CSession::AddProhibitionLog(std::uint32_t aid, std::uint32_t minutes) { + const CRenderObject* actor = static_cast(g_modeMgr.GetGameMode())->scene->GetGameActorByAID(aid); + if (!actor) { + return; + } + + std::uint32_t accountId = actor->render.accountId; + const std::string actorName = static_cast(g_modeMgr.GetGameMode())->ActorName(aid); + + ProhibitionLogEntry entry; + std::strcpy(entry.name, actorName.c_str()); + std::strcpy(entry.ownerName, this->m_serverName.c_str()); + entry.minutes = minutes; + entry.aid = aid; + entry.accountId = accountId; + + SYSTEMTIME localTime; + GetLocalTime(&localTime); + entry.date = ((localTime.wYear << 8 | localTime.wMonth) << 8) | localTime.wDay; + entry.time = ((localTime.wHour << 8 | localTime.wMinute) << 8) | localTime.wSecond; + this->m_prohibitionLog.push_back(entry); + + CFile file; + char path[256]; + GetCurrentDirectoryA(sizeof(path), path); + char* data = std::strstr(path, "\\data"); + int count = 0; + int size = 0; + if (data) { + std::memcpy(path, path, data - path); + } + std::strcat(path, "\\ProhibitionLog.gd"); + + char* buffer; + if (file.Open(path, 0, true, false)) { + size = file.GetSize() - 4; + buffer = new char[size]; + file.Read(&count, 4); + file.Read(buffer, size); + file.Close(); + } + + ++count; + file.Open(path, 0x1001, true, false); + file.Write(&count, 4); + if (size) { + file.Write(buffer, size); + } + + int length = static_cast(std::strlen(entry.name)) + 1; + file.Write(&length, 4); + file.Write(entry.name, length); + length = static_cast(std::strlen(entry.ownerName)) + 1; + file.Write(&length, 4); + file.Write(entry.ownerName, length); + file.Write(&aid, 4); + file.Write(&accountId, 4); + file.Write(&entry.minutes, 4); + file.Write(&entry.date, 4); + file.Write(&entry.time, 4); + file.Close(); + if (size) { + delete[] buffer; + } +} + +// OFFSET: 0x00E07FB0 +// FUNCTION: fn_23750 +int CSession::ProcessEmotionShortcut(const char* text) { + if (!text) { + return 0; + } + + CTokenizer tokenizer; + tokenizer.Parse(std::string(text)); + if (tokenizer.tokens.size() == 0) { + return 0; + } + + LowercaseText(tokenizer[0]); + return g_cashEmotionMgr->PlaySearchedEmotion(tokenizer[0]); +}