From c3ae049e9c054291d113ae1aa821aab1f050ff4b Mon Sep 17 00:00:00 2001 From: atom0s Date: Mon, 28 Nov 2016 15:55:40 -0800 Subject: [PATCH] Initial code commit. --- WatchEXP.sln | 22 + WatchEXP/CommandParse.h | 94 +++ WatchEXP/WatchEXP.cpp | 966 ++++++++++++++++++++++++++++++ WatchEXP/WatchEXP.h | 85 +++ WatchEXP/WatchEXP.vcxproj | 111 ++++ WatchEXP/WatchEXP.vcxproj.filters | 35 ++ WatchEXP/exports.def | 5 + 7 files changed, 1318 insertions(+) create mode 100644 WatchEXP.sln create mode 100644 WatchEXP/CommandParse.h create mode 100644 WatchEXP/WatchEXP.cpp create mode 100644 WatchEXP/WatchEXP.h create mode 100644 WatchEXP/WatchEXP.vcxproj create mode 100644 WatchEXP/WatchEXP.vcxproj.filters create mode 100644 WatchEXP/exports.def diff --git a/WatchEXP.sln b/WatchEXP.sln new file mode 100644 index 0000000..fceb160 --- /dev/null +++ b/WatchEXP.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.30501.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WatchEXP", "WatchEXP\WatchEXP.vcxproj", "{25DAD057-C948-487D-A69C-6540DCD565DA}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {25DAD057-C948-487D-A69C-6540DCD565DA}.Debug|Win32.ActiveCfg = Debug|Win32 + {25DAD057-C948-487D-A69C-6540DCD565DA}.Debug|Win32.Build.0 = Debug|Win32 + {25DAD057-C948-487D-A69C-6540DCD565DA}.Release|Win32.ActiveCfg = Release|Win32 + {25DAD057-C948-487D-A69C-6540DCD565DA}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/WatchEXP/CommandParse.h b/WatchEXP/CommandParse.h new file mode 100644 index 0000000..e162b58 --- /dev/null +++ b/WatchEXP/CommandParse.h @@ -0,0 +1,94 @@ + +#pragma once + +#ifndef __COMMANDPARSE_H_INCLUDED__ +#define __COMMANDPARSE_H_INCLUDED__ + +#include +#include + +#define VALIDATE_COMMAND( s, c ) \ + if (_strnicmp( s + 1, c, strlen( c ) ) != 0) \ + return false; + +#define HANDLE_ARGUMENT( a, b ) \ + if (a[1] == b) + +/** + * GetCommandArgs + * @param1 : The command to parse for arguments. + * @param2 : Array of strings to store the found arguments. + * @returns : Number of arguments found within the command. + * + * This function mimics the *nix command 'strsep' to locate and + * parse strings with inline-quotes. + * + * Thanks to: + * http://stackoverflow.com/questions/9659697/parse-string-into-array-based-on-spaces-or-double-quotes-strings + * + * Modified for Ashita needs with plugin commands. + * + */ +int __stdcall GetCommandArgs( const char* pszCommand, std::string* args ) +{ + enum { + NONE, + IN_WORD, + IN_STRING, + } state = NONE; + + char szCurrentArgument[ 255 ] = { 0 }; + char szCurrentChar = 0; + char *pszStart = 0; + int nArgCount = 0; + + for (const char* p = pszCommand; *p != '\0' || state != NONE; ++p) + { + szCurrentChar = (unsigned char)*p; + switch (state) + { + case NONE: + { + if (isspace( szCurrentChar )) + continue; + if (szCurrentChar == '"') + { + state = IN_STRING; + pszStart = (char*)( p + 1 ); + continue; + } + + state = IN_WORD; + pszStart = (char*)p; + continue; + } + + case IN_STRING: + if (szCurrentChar == '"') + { + strncpy_s( szCurrentArgument, pszStart, p - pszStart ); + args[nArgCount] = std::string( szCurrentArgument ); + nArgCount++; + + state = NONE; + } + continue; + + case IN_WORD: + if (isspace( szCurrentChar ) || *++p == '\0') + { + strncpy_s( szCurrentArgument, pszStart, p - pszStart ); + args[nArgCount] = std::string( szCurrentArgument ); + nArgCount++; + + state = NONE; + } + *--p; + continue; + } + } + + return nArgCount; +} + +#endif // __COMMANDPARSE_H_INCLUDED__ \ No newline at end of file diff --git a/WatchEXP/WatchEXP.cpp b/WatchEXP/WatchEXP.cpp new file mode 100644 index 0000000..71595f9 --- /dev/null +++ b/WatchEXP/WatchEXP.cpp @@ -0,0 +1,966 @@ +#include "WatchEXP.h" +#include "CommandParse.h" + +WatchEXP::WatchEXP(void) + : m_AshitaCore(nullptr) + , m_PluginId(0) + , m_Direct3DDevice(nullptr) + , m_ExpObj(nullptr) + , m_AbyTimeStart(0) + , m_DynTimeStart(0) + , m_BaseTimeEXP(0) + , m_BaseTimeCR(0) + , m_resetTime(NULL) + , font_size(0) + , m_reset_min(0) + , m_CurrentZoneID(0) + , m_LastZoneRecorded(0) + , m_Dyna_Crm('X') + , m_Dyna_Azr('X') + , m_Dyna_Amb('X') + , m_Dyna_Alab('X') + , m_Dyna_Obs('X') + , m_vTime(0) + , m_DynaTime(0) + , m_xpKill(0) + , m_xpTotal(0) + , m_xpTest(0) + , m_crKill(0) + , m_crTotal(0) + , m_prl(0) + , m_gld(0) + , m_slv(0) + , m_ebn(0) + , m_azr(0) + , m_rby(0) + , m_amb(0) + , backTrans(0) + , m_IsInAbyssea(false) + , m_IsInDynamis(false) + , show_XP_M_always(false) + , m_autoreset(false) +{ } +WatchEXP::~WatchEXP(void) +{ } + +plugininfo_t WatchEXP::GetPluginInfo(void) +{ + return *g_PluginInformation; +} + +bool WatchEXP::Initialize(IAshitaCore* core, ILogManager* log, uint32_t id) +{ + // Store the variables we are passed.. + this->m_AshitaCore = core; + this->m_PluginId = id; + + this->GetConfig(); + + return true; +} + +void WatchEXP::Release(void) +{ +} + +bool WatchEXP::HandleCommand(const char* pszCommand, int nCommandType) +{ + if (pszCommand == nullptr) + return false; + + if (_strnicmp(pszCommand, "/wexp", 4) == 0) + { + std::vector args; + Ashita::Commands::GetCommandArgs(pszCommand, &args); + + if (args[1] == "reset") + { + m_crTotal = 0, m_xpTotal = 0, m_xpKill = 0, m_crKill = 0; + m_BaseTimeEXP = time(nullptr), m_BaseTimeCR = time(nullptr); + + m_AshitaCore->GetChatManager()->Write("WatchEXP was reset!"); + return true; + } + if (args[1] == "config") + { + this->GetConfig(); + m_ExpObj->SetFontFamily(font_name); + m_ExpObj->SetFontHeight(font_size); + + return true; + } + } + return false; +} + +//bool WatchEXP::HandleNewChatLine(short sMode, char* pszChatLine) +bool WatchEXP::HandleIncomingText(int16_t sMode, const char* pszChatLine, int16_t* modifiedMode, char* modifiedMessage, bool blocked) +{ + std::string data; + std::vector< std::string > temp; + + data = ScrubChat(pszChatLine); + + //Holds matches + std::smatch m; + +#pragma region Search Pattern +//Regex Searches + std::regex xp_inc("gains\\s\\d+\\s(experience|limit)\\spoints"); + std::regex xp_chain("EXP\\schain\\s\\d+\\s\\w+\\sgains\\s\\d+\\sexperience\\spoints"); + std::regex cru_inc("obtained\\s\\d+\\scruor", std::regex_constants::icase); + std::regex vis_wear("Your\\svisitant\\sstatus\\swill\\swear\\soff\\sin\\s\\d+\\sminutes"); + std::regex vis_inc("Your\\svisitant\\sstatus\\shas\\sbeen\\sextended\\sby\\s\\d+\\sminutes"); + std::regex vis_exit("Exiting\\sin\\s\\d+\\sminutes"); + std::regex vis_pegs("Visitant\\sLight\\sIntensity\\sPearlescent\\s\\d+\\sEbon\\s\\d+\\sGolden\\s\\d+\\sSilvery\\s\\d+"); + std::regex vis_azram("Azure\\s\\d+\\sRuby\\s\\d+\\sAmber\\s\\d+"); + std::regex light_pearl("body\\semits\\sa\\sfaint\\spearlescent\\slight"); + std::regex light_azure("body\\semits\\sa\\sfeeble\\sazure\\slight"); + std::regex light_ruby("body\\semits\\sa\\sfeeble\\sruby\\slight"); + std::regex light_amber("body\\semits\\sa\\sfeeble\\samber\\slight"); + std::regex light_gold("body\\semits\\sa\\sfaint\\sgolden\\slight"); + std::regex light_silver("body\\semits\\sa\\sfaint\\ssilvery\\slight"); + std::regex light_ebon("body\\semits\\sa\\sfaint\\sebon\\slight"); + std::regex dyn_start("sands"); + std::regex dyn_start2("hourglass"); + std::regex dyn_extend("Dynamis"); + std::regex dyn_extend2("extended"); + std::regex dyn_expelled("expelled"); + + //Used to get all the numbers + std::regex get_number("\\d+"); + + if (sMode == 121) + { + if (std::regex_search(data, xp_chain)) + { + std::sregex_token_iterator start(data.begin(), data.end(), get_number, 0), end; + + while (start != end) + { + temp.push_back(*start); + ++start; + } + + if (temp.size() >= 1) + m_xpKill = std::stoi(temp[temp.size() - 1]); + else + m_xpKill = 0; + + m_xpTotal += m_xpKill; + } + } + if (sMode == 131) + { + if (std::regex_search(data, xp_inc)) + { + std::sregex_token_iterator start(data.begin(), data.end(), get_number, 0), end; + temp.clear(); + + while (start != end) + { + temp.push_back(*start); + ++start; + } + + if (temp.size() >= 1) + m_xpKill = std::stoi(temp[temp.size() - 1]); + else + m_xpKill = 0; + + m_xpTotal += m_xpKill; + } + } + else if (sMode == 633) + { + if (std::regex_search(data, cru_inc)) + { + std::sregex_token_iterator start(data.begin(), data.end(), get_number, 0), end; + temp.clear(); + + while (start != end) + { + temp.push_back(*start); + ++start; + } + + if (temp.size() >= 1) + m_crKill = std::stoi(temp[temp.size() - 1]); + else + m_crKill = 0; + + m_crTotal += m_crKill; + } + else if (std::regex_search(data, vis_inc)) + { + m_vTime += 10; + } + } + else if (sMode == 653) + { + if ((std::regex_search(data, dyn_expelled)) && (std::regex_search(data, dyn_extend))) + { + std::regex_search(data, m, get_number); + + m_DynaTime = std::stoi(m.str()); + + m_DynTimeStart = time(nullptr); + } + } + else if (sMode == 658) + { + if ((std::regex_search(data, dyn_extend)) && (std::regex_search(data, dyn_extend2))) + { + std::regex_search(data, m, get_number); + + m_DynaTime += std::stoi(m.str()); + + m_DynTimeStart = time(nullptr); + } + } + else if (sMode == 660) + { + if ((std::regex_search(data, dyn_start)) && (std::regex_search(data, dyn_start2))) + { + std::regex_search(data, m, get_number); + + m_DynaTime = std::stoi(m.str()); + } + if (std::regex_search(data, vis_exit)) + { + std::regex_search(data, m, get_number); + + m_vTime = std::stoi(m.str()); + } + else if (std::regex_search(data, cru_inc)) + { + std::regex_search(data, m, get_number); + m_crKill = std::stoi(m.str()); + + m_crTotal += m_crKill; + } + else if (std::regex_search(data, vis_wear)) + { + std::regex_search(data, m, get_number); + m_vTime = std::stoi(m.str()); + + m_AbyTimeStart = time(nullptr); + } + else if (std::regex_search(data, vis_pegs)) + { + std::sregex_token_iterator start(data.begin(), data.end(), get_number, 0), end; + + while (start != end) + { + temp.push_back(*start); + ++start; + } + + //Corrects Pearlescent Values + m_prl = std::stoi(temp[0]); + + //Corrects Ebon Values + m_ebn = std::stoi(temp[1]); + + //Corrects Gold Values + m_gld = std::stoi(temp[2]); + + //Corrects Silvery Values + m_slv = std::stoi(temp[3]); + + } + else if (std::regex_search(data, vis_azram)) + { + std::sregex_token_iterator start(data.begin(), data.end(), get_number, 0), end; + + while (start != end) + { + temp.push_back(*start); + ++start; + } + + //Corrects Azure Values + m_azr = std::stoi(temp[0]); + + //Corrects Ruby Values + m_rby = std::stoi(temp[1]); + + //Corrects Amber Values + m_amb = std::stoi(temp[2]); + + } + else if (std::regex_search(data, light_pearl)) + { + //checks to ensure value does not exceed 230 + if (m_prl + 5 > 230) + { + m_prl = 230; + } + else + { + m_prl += 5; + } + } + else if (std::regex_search(data, light_gold)) + { + //checks to ensure value does not exceed 200 + if (m_gld + 5 > 200) + { + m_gld = 200; + } + else + { + m_gld += 5; + } + } + else if (std::regex_search(data, light_silver)) + { + //checks to ensure value does not exceed 200 + if (m_slv + 5 > 200) + { + m_slv = 200; + } + else + { + m_slv += 5; + } + } + else if (std::regex_search(data, light_ebon)) + { + //checks to ensure value does not exceed 200 + if (m_ebn + 1 > 200) + { + m_ebn = 200; + } + else + { + m_ebn += 1; + } + } + else if (std::regex_search(data, light_azure)) + { + //checks to ensure value does not exceed 255 + if (m_azr + 8 > 255) + { + m_azr = 255; + } + else + { + m_azr += 8; + } + } + else if (std::regex_search(data, light_ruby)) + { + //checks to ensure value does not exceed 255 + if (m_rby + 8 > 255) + { + m_rby = 255; + } + else + { + m_rby += 8; + } + } + else if (std::regex_search(data, light_amber)) + { + //checks to ensure value does not exceed 255 + if (m_amb + 8 > 255) + { + m_amb = 255; + } + else + { + m_amb += 8; + } + } + } +#pragma endregion + + return false; +} + +bool WatchEXP::Direct3DInitialize(IDirect3DDevice8* lpDevice) +{ + this->m_Direct3DDevice = lpDevice; + + m_ExpObj = m_AshitaCore->GetFontManager()->Create("EXP_ID"); + m_ExpObj->SetText("0/0"); + m_ExpObj->SetFontFamily(font_name); + m_ExpObj->SetFontHeight(font_size); + m_ExpObj->SetColor(0xFFFFFFFF); + m_ExpObj->SetBold(false); + m_ExpObj->SetVisibility(true); + m_ExpObj->SetPositionX(100); + m_ExpObj->SetPositionY(100); + m_ExpObj->SetVisibility(false); + + m_ExpObj->GetBackground()->SetVisibility(true); + m_ExpObj->GetBackground()->SetColor(0x80000000); + + m_BaseTimeEXP = time(nullptr); + m_BaseTimeCR = time(nullptr); + + return true; +} + +void WatchEXP::Direct3DRelease(void) +{ + m_AshitaCore->GetFontManager()->Delete("EXP_ID"); +} + +void WatchEXP::Direct3DRender(void) +{ + doEXPCalculations(); + doAbysseaZoneTimings(); + doDynamisZoneTimings(); + checkResetTime(); +} + +__declspec(dllexport) double __stdcall GetInterfaceVersion(void) +{ + return ASHITA_INTERFACE_VERSION; +} + +__declspec(dllexport) void __stdcall CreatePluginInfo(plugininfo_t* info) +{ + g_PluginInformation = info; + + strcpy_s(info->Author, sizeof(info->Author), "Praenuntiae (ported by Vicrelant)"); + strcpy_s(info->Name, sizeof(info->Name), "WatchEXP"); + info->InterfaceVersion = ASHITA_INTERFACE_VERSION; + info->PluginVersion = 3.0f; + info->Priority = 0; +} + +__declspec(dllexport) IPlugin* __stdcall CreatePlugin(void) +{ + return (IPlugin*)new WatchEXP(); +} + +#pragma region GetConfig +void WatchEXP::GetConfig() +{ + auto iConfigResult = false; + auto iTemp = 0; + + iConfigResult = m_AshitaCore->GetConfigurationManager()->Load("WatchEXP", "WatchEXP"); + + if (iConfigResult != 0) + { + auto szBuffer = m_AshitaCore->GetConfigurationManager()->get_string("WatchEXP", "font_name"); + strcpy_s(font_name, szBuffer); + + font_size = m_AshitaCore->GetConfigurationManager()->get_int32("WatchEXP", "font_size", 11); + show_XP_M_always = m_AshitaCore->GetConfigurationManager()->get_bool("WatchEXP", "show_both_xp_and_merits_when_capped", false); + m_reset_min = m_AshitaCore->GetConfigurationManager()->get_int32("WatchEXP", "auto_reset_time", 45); + m_autoreset = m_AshitaCore->GetConfigurationManager()->get_bool("WatchEXP", "auto_reset_enabled", false); + } + else if (iConfigResult == 0) + { + m_AshitaCore->GetConfigurationManager()->set_value("WatchEXP", "font_name", "Arial"); + m_AshitaCore->GetConfigurationManager()->set_value("WatchEXP", "font_size", "11"); + m_AshitaCore->GetConfigurationManager()->set_value("WatchEXP", "show_both_xp_and_merits_when_capped", "true"); + m_AshitaCore->GetConfigurationManager()->set_value("WatchEXP", "auto_reset_time", "45"); + m_AshitaCore->GetConfigurationManager()->set_value("WatchEXP", "auto_reset_enabled", "false"); + m_AshitaCore->GetConfigurationManager()->Save("WatchEXP", "WatchEXP"); + + strcpy_s(font_name, "Arial"); + font_size = 11; + show_XP_M_always = false; + m_reset_min = 45; + m_autoreset = false; + } +} +#pragma endregion + +#pragma region doEXPCalculations +void WatchEXP::doEXPCalculations() +{ + char *p; + char expIntoLVL[10] = "", expForLVL[10] = "", expTillLVL[10] = "", lmtIntoLVL[10] = "", lmtTillLVL[10] = "", xpTotal[40] = "", crTotal[40] = ""; + + m_IsInAbyssea = isInAbyssea(m_AshitaCore->GetDataManager()->GetParty()->GetMemberZone(0)); + m_IsInDynamis = isInDynamis(m_AshitaCore->GetDataManager()->GetParty()->GetMemberZone(0)); + + // get the EXP info + unsigned short expIN = m_AshitaCore->GetDataManager()->GetPlayer()->GetExpCurrent(); + unsigned short expMAX = m_AshitaCore->GetDataManager()->GetPlayer()->GetExpNeeded(); + unsigned short expLEFT = expMAX - expIN; + + // get the Merit info + unsigned short lmtIN = m_AshitaCore->GetDataManager()->GetPlayer()->GetLimitPoints(); + unsigned short lmtMAX = 10000; + unsigned short lmtLEFT = lmtMAX - lmtIN; + + p = expIntoLVL; + makeComma(expIN, p, 0); + _strrev(expIntoLVL); + + p = expForLVL; + makeComma(expMAX, p, 0); + _strrev(expForLVL); + + p = expTillLVL; + makeComma(expLEFT, p, 0); + _strrev(expTillLVL); + + p = lmtIntoLVL; + makeComma(lmtIN, p, 0); + _strrev(lmtIntoLVL); + + p = lmtTillLVL; + makeComma(lmtLEFT, p, 0); + _strrev(lmtTillLVL); + + p = xpTotal; + makeComma(m_xpTotal, p, 0); + _strrev(xpTotal); + + p = crTotal; + makeComma(m_crTotal, p, 0); + _strrev(crTotal); + + if (m_AshitaCore->GetDataManager()->GetPlayer()->GetMainJobLevel() < 75) + { + if (m_IsInDynamis) + { + sprintf_s(TextObjectBuffer, MAX_PATH, " %s/%s(%s) XP:%s(%u) %.1fk/hr Alb:%c Amb:%c Azr:%c Crm:%c Obn:%c Time Remaining: %d ", expIntoLVL, expForLVL, expTillLVL, + xpTotal, m_xpKill, returnProjectedEXPperHour(m_xpTotal), m_Dyna_Alab, m_Dyna_Amb, m_Dyna_Azr, m_Dyna_Crm, m_Dyna_Obs, m_DynaTime); + } + else if (m_IsInAbyssea) + { + sprintf_s(TextObjectBuffer, MAX_PATH, " %s/%s(%s) XP:%s(%u) %.1fk/hr Cruor:%s(%u) %.1fk/hr Prl:%u Gld:%u Slv:%u Ebn:%u Azr:%u Rby:%u Amb:%u V.Time: %d ", + expIntoLVL, expForLVL, expTillLVL, xpTotal, m_xpKill, returnProjectedEXPperHour(m_xpTotal), crTotal, m_crKill, returnProjectedCRUORperHour(m_crTotal), + m_prl, m_gld, m_slv, m_ebn, m_azr, m_rby, m_amb, m_vTime); + } + else + { + sprintf_s(TextObjectBuffer, MAX_PATH, " %s/%s(%s) XP:%s(%u) %.1fk/hr ", expIntoLVL, expForLVL, expTillLVL, + xpTotal, m_xpKill, returnProjectedEXPperHour(m_xpTotal)); + } + } + if ((!show_XP_M_always) && (m_AshitaCore->GetDataManager()->GetPlayer()->GetMainJobLevel() >= 75)) + { + unsigned char isMeritMode = m_AshitaCore->GetDataManager()->GetPlayer()->GetLimitMode(); + + if ((expLEFT == 1) || (isMeritMode == 224)) + { + if (m_IsInDynamis) + { + sprintf_s(TextObjectBuffer, MAX_PATH, " %s/10,000(%s)[%i] XP:%s(%u) %.1fk/hr Alb:%c Amb:%c Azr:%c Crm:%c Obn:%c Time Remaining: %d ", lmtIntoLVL, lmtTillLVL, + (int)m_AshitaCore->GetDataManager()->GetPlayer()->GetMeritPoints(), xpTotal, m_xpKill, returnProjectedEXPperHour(m_xpTotal), m_Dyna_Alab, m_Dyna_Amb, m_Dyna_Azr, m_Dyna_Crm, + m_Dyna_Obs, m_DynaTime); + } + else if (m_IsInAbyssea) + { + sprintf_s(TextObjectBuffer, MAX_PATH, " %s/10,000(%s)[%i] XP:%s(%u) %.1fk/hr Cruor:%s(%u) %.1fk/hr Prl:%u Gld:%u Slv:%u Ebn:%u Azr:%u Rby:%u Amb:%u V.Time: %d ", lmtIntoLVL, lmtTillLVL, + (int)m_AshitaCore->GetDataManager()->GetPlayer()->GetMeritPoints(), xpTotal, m_xpKill, returnProjectedEXPperHour(m_xpTotal), crTotal, m_crKill, returnProjectedCRUORperHour(m_crTotal), + m_prl, m_gld, m_slv, m_ebn, m_azr, m_rby, m_amb, m_vTime); + } + else + { + sprintf_s(TextObjectBuffer, MAX_PATH, " %s/10,000(%s)[%i] XP:%s(%u) %.1fk/hr ", + lmtIntoLVL, lmtTillLVL, (int)m_AshitaCore->GetDataManager()->GetPlayer()->GetMeritPoints(), xpTotal, + m_xpKill, returnProjectedEXPperHour(m_xpTotal)); + } + } + else + { + if (m_IsInDynamis) + { + sprintf_s(TextObjectBuffer, MAX_PATH, " %s/%s(%s) XP:%s(%u) %.1fk/hr Alb:%c Amb:%c Azr:%c Crm:%c Obn:%c Time Remaining: %d ", expIntoLVL, expForLVL, expTillLVL, + xpTotal, m_xpKill, returnProjectedEXPperHour(m_xpTotal), m_Dyna_Alab, m_Dyna_Amb, m_Dyna_Azr, m_Dyna_Crm, m_Dyna_Obs, m_DynaTime); + } + else if (m_IsInAbyssea) + { + sprintf_s(TextObjectBuffer, MAX_PATH, " %s/%s(%s) XP:%s(%u) %.1fk/hr Cruor:%s(%u) %.1fk/hr Prl:%u Gld:%u Slv:%u Ebn:%u Azr:%u Rby:%u Amb:%u V.Time: %d ", + expIntoLVL, expForLVL, expTillLVL, xpTotal, m_xpKill, returnProjectedEXPperHour(m_xpTotal), crTotal, m_crKill, returnProjectedCRUORperHour(m_crTotal), m_prl, + m_gld, m_slv, m_ebn, m_azr, m_rby, m_amb, m_vTime); + } + else + { + sprintf_s(TextObjectBuffer, MAX_PATH, " %s/%s(%s) XP:%s(%u) %.1fk/hr ", expIntoLVL, expForLVL, expTillLVL, xpTotal, m_xpKill, returnProjectedEXPperHour(m_xpTotal)); + } + } + } + if ((show_XP_M_always) && (m_AshitaCore->GetDataManager()->GetPlayer()->GetMainJobLevel() >= 75)) + { + if (m_IsInDynamis) + { + sprintf_s(TextObjectBuffer, MAX_PATH, " %s/%s(%s) %s/10,000(%s)[%i] XP:%s(%u) %.1fk/hr Alb:%c Amb:%c Azr:%c Crm:%c Obn:%c Time Remaining: %d ", expIntoLVL, expForLVL, expTillLVL, + lmtIntoLVL, lmtTillLVL, (int)m_AshitaCore->GetDataManager()->GetPlayer()->GetMeritPoints(), xpTotal, m_xpKill, returnProjectedEXPperHour(m_xpTotal), m_Dyna_Alab, m_Dyna_Amb, + m_Dyna_Azr, m_Dyna_Crm, m_Dyna_Obs, m_DynaTime); + } + else if (m_IsInAbyssea) + { + sprintf_s(TextObjectBuffer, MAX_PATH, " %s/%s(%s) %s/10,000(%s)[%i] XP:%s(%u) %.1fk/hr Cruor:%s(%u) %.1fk/hr Prl:%u Gld:%u Slv:%u Ebn:%u Azr:%u Rby:%u Amb:%u V.Time: %d ", + expIntoLVL, expForLVL, expTillLVL, lmtIntoLVL, lmtTillLVL, (int)m_AshitaCore->GetDataManager()->GetPlayer()->GetMeritPoints(), xpTotal, m_xpKill, returnProjectedEXPperHour(m_xpTotal), + crTotal, m_crKill, returnProjectedCRUORperHour(m_crTotal), m_prl, m_gld, m_slv, m_ebn, m_azr, m_rby, m_amb, m_vTime); + } + else + { + sprintf_s(TextObjectBuffer, MAX_PATH, " %s/%s(%s) %s/10,000(%s)[%i] XP:%s(%u) %.1fk/hr ", expIntoLVL, expForLVL, expTillLVL, + lmtIntoLVL, lmtTillLVL, (int)m_AshitaCore->GetDataManager()->GetPlayer()->GetMeritPoints(), xpTotal, m_xpKill, returnProjectedEXPperHour(m_xpTotal)); + } + } + + // double check the size of the text + if (m_ExpObjSize.cy == 0) + { + int window_y = m_AshitaCore->GetConfigurationManager()->get_int32("boot_config", "window_y", 600); + m_ExpObj->GetTextSize(&m_ExpObjSize); + m_ExpObj->SetPositionX(15.0f); + m_ExpObj->SetPositionY((float)(window_y - m_ExpObjSize.cy)); + } + + // set the object information + m_ExpObj->SetText(TextObjectBuffer); + m_ExpObj->SetVisibility(true); +} +#pragma endregion +#pragma region returnProjectedEXPperHour +float WatchEXP::returnProjectedEXPperHour(unsigned int expIntoLevel) +{ + float changeInTime = (float(time(nullptr) - m_BaseTimeEXP)); + float changeInTimeHR = ((changeInTime / 60) / 60); + + if (expIntoLevel == 0) + { + m_BaseTimeEXP = time(nullptr); + } + + // just in case we don't do a divide by zero + if (changeInTime == 0.0f) + { + return 0; + } + + return ((((float)expIntoLevel) / changeInTimeHR) / 1000); +} +#pragma endregion +#pragma region returnProjectedCRUORperHour +float WatchEXP::returnProjectedCRUORperHour(unsigned int cruorLVL) +{ + float changeInTime = (float(time(nullptr) - m_BaseTimeCR)); + float changeInTimeHR = ((changeInTime / 60) / 60); + + if (cruorLVL == 0) + { + m_BaseTimeCR = time(nullptr); + } + + // just in case we don't do a divide by zero + if (changeInTime == 0.0f) + { + return 0; + } + + return ((((float)cruorLVL) / changeInTimeHR) / 1000); +} +#pragma endregion +#pragma region isInAbyssea +bool WatchEXP::isInAbyssea(int zoneID) +{ + // look for it + for (auto& x : AbysseaZoneIDs) + { + // Yes! + if (x == zoneID) + return true; + } + + m_prl = 0, m_gld = 0, m_amb = 0, m_slv = 0, m_azr = 0, m_ebn = 0, m_rby = 0, m_vTime = 0; + + return false; +} +#pragma endregion +#pragma region isInDynamis +bool WatchEXP::isInDynamis(int zoneID) +{ + // look for it + for (auto& x : DynamisZoneIDs) + { + // Yes! + if (x == zoneID) + return true; + } + + m_Dyna_Crm = 'X', m_Dyna_Azr = 'X', m_Dyna_Alab = 'X', m_Dyna_Amb = 'X', m_Dyna_Obs = 'X'; + // Nope + return false; +} +#pragma endregion +#pragma region doAbysseaZoneTimings +void WatchEXP::doAbysseaZoneTimings() +{ + // check to see if we are in Abyssea + m_IsInAbyssea = isInAbyssea(m_AshitaCore->GetDataManager()->GetParty()->GetMemberZone(0)); + + if (m_IsInAbyssea && (time(nullptr) - m_AbyTimeStart) > 60) + { + m_AbyTimeStart = time(nullptr); + if (m_vTime != 0) + { + m_vTime -= 1; + } + } +} +#pragma endregion +#pragma region doDynamisZoneTimings +void WatchEXP::doDynamisZoneTimings() +{ + // check to see if we are in Abyssea + m_IsInDynamis = isInDynamis(m_AshitaCore->GetDataManager()->GetParty()->GetMemberZone(0)); + + doDynamisKItemCheck(); + + if (m_IsInDynamis && (time(nullptr) - m_DynTimeStart) > 60) + { + m_DynTimeStart = time(nullptr); + if (m_DynaTime != 0) + { + m_DynaTime -= 1; + } + } +} +#pragma endregion +#pragma region doDynamisKItemCheck +void WatchEXP::doDynamisKItemCheck() +{ + //Checks for Crimson Granule of Time + + if (m_AshitaCore->GetDataManager()->GetPlayer()->HasKeyItem(1545)) + { + m_Dyna_Crm = 'O'; + } + //Checks for Azure Granule of Time + if (m_AshitaCore->GetDataManager()->GetPlayer()->HasKeyItem(1546)) + { + m_Dyna_Azr = 'O'; + } + //Checks for Amber Granule of Time + if (m_AshitaCore->GetDataManager()->GetPlayer()->HasKeyItem(1547)) + { + m_Dyna_Amb = 'O'; + } + //Checks for Alabaster Granule of Time + if (m_AshitaCore->GetDataManager()->GetPlayer()->HasKeyItem(1548)) + { + m_Dyna_Alab = 'O'; + } + //Checks for Obsidian Granule of Time + if (m_AshitaCore->GetDataManager()->GetPlayer()->HasKeyItem(1549)) + { + m_Dyna_Obs = 'O'; + } +} +#pragma endregion +#pragma region makeComma +void WatchEXP::makeComma(int n, char *p, int count) +{ + int d; + // Get least significant digit + d = n % 10; + // Convert to ascii character and store + *p++ = d + '0'; + // Remove least significant digit + // Test for finished + if (n /= 10) + { + // Not finished, increase digit count, + // test for comma position + count++; + if (count >= 3) + { + // Add the comma and reset the count + *p++ = ','; + count = 0; + } + // Call this procedure to work on + // remaining part of number + makeComma(n, p, count); + } + return; +} +#pragma endregion + +#pragma region convertFromHex +int WatchEXP::convertFromHex(std::string hex) +{ + int value = 0; + int a = 0; + int b = hex.length() - 1; + + for (; b >= 0; a++, b--) + { + if (hex[b] >= '0' && hex[b] <= '9') + { + value += (hex[b] - '0') * (1 << (a * 4)); + } + else + { + switch (hex[b]) + { + case 'A': + + case 'a': + value += 10 * (1 << (a * 4)); + break; + + case 'B': + + case 'b': + value += 11 * (1 << (a * 4)); + break; + + case 'C': + + case 'c': + value += 12 * (1 << (a * 4)); + break; + + case 'D': + + case 'd': + value += 13 * (1 << (a * 4)); + break; + + case 'E': + + case 'e': + value += 14 * (1 << (a * 4)); + break; + + case 'F': + + case 'f': + value += 15 * (1 << (a * 4)); + break; + + default: + + break; + + } + } + } + return value; +} +#pragma endregion +#pragma region hextodec +void WatchEXP::hextodec(std::string hex, std::vector& rgb) +{ + std::string redString, greenString, blueString; + + if (hex.compare(0, 1, "#") == 0) + { + //If the prefix # was attached to hex, use the following code + redString = hex.substr(1, 2); + greenString = hex.substr(3, 2); + blueString = hex.substr(5, 2); + } + else if (hex.compare(0, 2, "0x") == 0) + { + //If the prefix 0x was attached to hex, use the following code + redString = hex.substr(2, 2); + greenString = hex.substr(4, 2); + blueString = hex.substr(6, 2); + } + else + { + //If there is no prefix attached to hex, use this code + redString = hex.substr(0, 2); + greenString = hex.substr(2, 2); + blueString = hex.substr(4, 2); + } + + unsigned char red = (unsigned char)(convertFromHex(redString)); + unsigned char green = (unsigned char)(convertFromHex(greenString)); + unsigned char blue = (unsigned char)(convertFromHex(blueString)); + + rgb[0] = red; + rgb[1] = green; + rgb[2] = blue; +} +#pragma endregion +#pragma region convertTrans +void WatchEXP::convertTrans(unsigned char& trans) +{ + float temp1, temp2; + + temp1 = (float)trans; + + temp2 = temp1 * (float)2.55; + + if (temp2 > 255) + { + trans = (unsigned char)255; + } + else + { + trans = (unsigned char)temp2; + } +} +#pragma endregion +#pragma region checkResetTime +void WatchEXP::checkResetTime() +{ + if (time(nullptr) - m_resetTime > 60) + { + if (m_xpTest != m_xpTotal) + { + m_xpTest = m_xpTotal; + m_resetTime = time(nullptr); + } + else if ((m_xpTest == m_xpTotal) && (m_autoreset)) + { + if (((time(nullptr) - m_resetTime) / 60) >= m_reset_min) + { + m_resetTime = time(nullptr); + + m_crTotal = 0, m_xpTotal = 0, m_xpKill = 0, m_crKill = 0; + } + } + } +} +#pragma endregion +#pragma region ScrubChat +std::string WatchEXP::ScrubChat(const char* chat_line) +{ + //strings to hold the chat data through the various scrubbing stages + std::string chat_raw; + std::string clean_chat_pass1; + std::string clean_chat_pass2; + std::string clean_chat_pass3; + std::string clean_chat_final; + + char szClean[1024]; + + //regex expressions for parsing the raw chat until it's clean + std::regex parse1("\\x1E\\x05|\\x1E(\\x03|\\x02|\\x01)|\\x7F\\x31|\\x1F(\\x79|\\x7F|\\x7B|\\W)"); + std::regex parse2("[[:cntrl:]]|\\W\\W"); + std::regex time_stamp("\\x1E\\x03((\\[\\d+(:|/)\\d+(:|/)\\d+(\\]| [AMP]+\\]))|\\[\\d+(:|/)\\d+(:|/)\\d+ - \\d+:\\d+:\\d+(\\]| [AMP]+\\]))\\x1E\\x01\\s+"); + std::regex multi_space("\\s+"); + + memset(szClean, 0, 1024); + + //Cleans up any auto translate data so it can be properly parsed + m_AshitaCore->GetChatManager()->ParseAutoTranslate(chat_line, szClean, 1024, false); + + chat_raw = chat_line; + + //Removes TimeStamp data from beginning of line + std::regex_replace(std::back_inserter(clean_chat_pass1), chat_raw.begin(), chat_raw.end(), time_stamp, ""); + + //Removes some control characters from the beginning and end of the string that print as garbage + std::regex_replace(std::back_inserter(clean_chat_pass2), clean_chat_pass1.begin(), clean_chat_pass1.end(), parse1, ""); + + //Removes any remaining control characters and replaces them with spaces + std::regex_replace(std::back_inserter(clean_chat_pass3), clean_chat_pass2.begin(), clean_chat_pass2.end(), parse2, " "); + + //Changes any multi space areas to single spaces + std::regex_replace(std::back_inserter(clean_chat_final), clean_chat_pass3.begin(), clean_chat_pass3.end(), multi_space, " "); + + return clean_chat_final; +} +#pragma endregion \ No newline at end of file diff --git a/WatchEXP/WatchEXP.h b/WatchEXP/WatchEXP.h new file mode 100644 index 0000000..2e89859 --- /dev/null +++ b/WatchEXP/WatchEXP.h @@ -0,0 +1,85 @@ +#ifndef __ASHITA_WATCHEXP_H_INCLUDED__ +#define __ASHITA_WATCHEXP_H_INCLUDED__ + +#if defined (_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include "G:\Code\git.ashita.atom0s.com\Ashita v3\Ashita-src\build\plugins\ADK\Ashita.h" +#include +#include +#include +#include +#include +#include +#include + +plugininfo_t* g_PluginInformation = nullptr; + +int AbysseaZoneIDs[] = { 15,45,132,215,216,217,218,253,254,255 }; +int DynamisZoneIDs[] = { 39,40,41,42,134,135,185,186,187,188 }; + +class WatchEXP : IPlugin +{ + WatchEXP(const WatchEXP & cpy); + WatchEXP & operator=(WatchEXP & rhs); + + IAshitaCore* m_AshitaCore; + DWORD m_PluginId; + IDirect3DDevice8* m_Direct3DDevice; + +public: + WatchEXP(void); + virtual ~WatchEXP(void); + +public: + plugininfo_t GetPluginInfo(void) override; + +private: + IFontObject* m_ExpObj; + + char TextObjectBuffer[MAX_PATH]; + + time_t m_AbyTimeStart, m_DynTimeStart, m_BaseTimeEXP, m_BaseTimeCR, m_resetTime; + + SIZE m_ExpObjSize; + int font_size, m_reset_min; + int m_CurrentZoneID, m_LastZoneRecorded; + char font_name[128]; + char m_Dyna_Crm, m_Dyna_Azr, m_Dyna_Amb, m_Dyna_Alab, m_Dyna_Obs; + std::string hexColor; + unsigned int m_vTime; + unsigned int m_DynaTime; + unsigned int m_xpKill, m_xpTotal, m_xpTest; + unsigned int m_crKill, m_crTotal; + unsigned int m_prl, m_gld, m_slv, m_ebn, m_azr, m_rby, m_amb; + unsigned char backTrans; + bool m_IsInAbyssea, m_IsInDynamis, show_XP_M_always, m_autoreset; +public: + bool Initialize(IAshitaCore* core, ILogManager* log, uint32_t id) override; + void Release(void) override; + bool HandleCommand(const char* pszCommand, int nCommandType) override; + bool HandleIncomingText(int16_t mode, const char* message, int16_t* modifiedMode, char* modifiedMessage, bool blocked) override; + bool Direct3DInitialize(IDirect3DDevice8* lpDevice) override; + void Direct3DRelease(void) override; + void Direct3DRender(void) override; + + void GetConfig(void); + + float returnProjectedEXPperHour(unsigned int expIntoLevel); + float returnProjectedCRUORperHour(unsigned int expIntoLevel); + void doAbysseaZoneTimings(); + void doDynamisZoneTimings(); + void doDynamisKItemCheck(); + void doEXPCalculations(); + bool isInAbyssea(int zoneID); + bool isInDynamis(int zoneID); + void makeComma(int n, char *p, int count); + int convertFromHex(std::string hex); + void hextodec(std::string hex, std::vector& rgb); + void convertTrans(unsigned char& trans); + void checkResetTime(); + std::string ScrubChat(const char* chat_line); +}; + +#endif \ No newline at end of file diff --git a/WatchEXP/WatchEXP.vcxproj b/WatchEXP/WatchEXP.vcxproj new file mode 100644 index 0000000..020ae5c --- /dev/null +++ b/WatchEXP/WatchEXP.vcxproj @@ -0,0 +1,111 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {25DAD057-C948-487D-A69C-6540DCD565DA} + Win32Proj + WatchEXP + + + + DynamicLibrary + true + v140_xp + MultiByte + + + DynamicLibrary + false + v140_xp + true + MultiByte + + + + + + + + + + + + + true + + + false + $(SolutionDir)$(Configuration)\Plugins\ + + + + + + Level3 + WIN32;_DEBUG;_WINDOWS;_USRDLL;WATCHEXP_EXPORTS;%(PreprocessorDefinitions) + true + ProgramDatabase + true + true + false + Async + + + Windows + true + exports.def + + + + + Level3 + + + Full + false + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;WATCHEXP_EXPORTS;%(PreprocessorDefinitions) + true + true + true + Async + + + Windows + true + true + true + exports.def + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/WatchEXP/WatchEXP.vcxproj.filters b/WatchEXP/WatchEXP.vcxproj.filters new file mode 100644 index 0000000..366c0d0 --- /dev/null +++ b/WatchEXP/WatchEXP.vcxproj.filters @@ -0,0 +1,35 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + + + Source Files + + + + + Source Files + + + \ No newline at end of file diff --git a/WatchEXP/exports.def b/WatchEXP/exports.def new file mode 100644 index 0000000..d949fcb --- /dev/null +++ b/WatchEXP/exports.def @@ -0,0 +1,5 @@ +LIBRARY "WatchEXP" +EXPORTS + GetInterfaceVersion + CreatePluginInfo + CreatePlugin \ No newline at end of file