Lolwutt
7 years ago
18 changed files with 1283 additions and 0 deletions
@ -0,0 +1,16 @@ |
|||||||
|
Multisend is a replacement for servo. No synchronization is needed, just load and go. |
||||||
|
Commands are as follows(all can be prefixed with /ms or /multisend): |
||||||
|
|
||||||
|
/ms send [command] - Sends the command to all characters with MultiSend loaded. |
||||||
|
/ms sendto CharName [command] - Sends the command to a specific character, if they have MultiSend loaded. |
||||||
|
/ms sendgroup GroupName [command] - Sends the command to all characters listed in a group. Groups can be defined in Ashita/Config/MultiSend.xml. See XML Structure file for example. |
||||||
|
/ms followme on/off - Enables or disables followme. When followme is enabled, any characters with follow enabled will follow the player who last activated followme. |
||||||
|
/ms follow on/off - Enables or disables follow. Any character with follow enabled with follow the player who last activated followme. |
||||||
|
/ms ignoreself on/off - When enabled, any command that would effect the local character is not executed by the local character. |
||||||
|
/ms debug on/off - When enabled, all received commands are printed to log and any character with followme updated will spam log with position updates. For debug purposes, obviously. |
||||||
|
/ms reload - Will reload groups xml. |
||||||
|
/ms help - Print an ingame reference. |
||||||
|
|
||||||
|
You may use [t] [me] or [p0] [p1] [p2] [p3] [p4] [p5] to replace the <> equivalents, if you wish to resolve them on local character. This will replace them with the appropriate server IDs, allowing the command to resolve on receiving character. |
||||||
|
|
||||||
|
Example: Typing /ms send /ma "Fire IV" [t] will make all characters with multisend loaded cast Fire IV on the character who typed the command's target, rather than their own current target. |
@ -0,0 +1,10 @@ |
|||||||
|
<multisend> <!--You must have an xml structure listed.--> |
||||||
|
<group index="1" name="BLMs"> <!--Every group must have a unique index, from 1 to 65535. It has no meaning, but it's used to maintain sync on reload. Using the same index on multiple groups will cause unexpected behavior. You may only use letters, no numbers symbols or spaces, in group names.--> |
||||||
|
<char>Avesta</char> <!--List all characters belonging to said group.--> |
||||||
|
<char>Stanlee</char> |
||||||
|
</group> |
||||||
|
<group index="2" name="Melee"> <!--Just another example..--> |
||||||
|
<char>Deadpool</char> |
||||||
|
<char>Wolverine</char> |
||||||
|
</group> |
||||||
|
</multisend> |
Binary file not shown.
@ -0,0 +1,241 @@ |
|||||||
|
#include "MultiSend.h" |
||||||
|
|
||||||
|
bool MultiSend::HandleCommand(const char* command, int32_t type) |
||||||
|
{ |
||||||
|
std::string arg; |
||||||
|
const char* com = command + GetArg(command, &arg); |
||||||
|
if ((_stricmp(arg.c_str(), "/ms")) |
||||||
|
&& (_stricmp(arg.c_str(), "/multisend"))) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if (strlen(com) < 2) return true; |
||||||
|
com++; |
||||||
|
com += GetArg(com, &arg); |
||||||
|
|
||||||
|
|
||||||
|
if (_stricmp(arg.c_str(), "send") == 0) |
||||||
|
{ |
||||||
|
if (strlen(com) < 2) return true; |
||||||
|
SendCommand(0xFFFF0000, com + 1); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
if (_stricmp(arg.c_str(), "sendto") == 0) |
||||||
|
{ |
||||||
|
if (strlen(com) < 2) |
||||||
|
{ |
||||||
|
m_AshitaCore->GetChatManager()->Write("MultiSend: Invalid command."); |
||||||
|
return true; |
||||||
|
} |
||||||
|
com++; |
||||||
|
com += GetArg(com, &arg); |
||||||
|
|
||||||
|
Claim(&(MMF_Pointer->Name.ProcessID), 0); |
||||||
|
for (int x = 0; x < 100; x++) |
||||||
|
{ |
||||||
|
if (MMF_Pointer->Name.Names[x].Active) |
||||||
|
{ |
||||||
|
if (_stricmp((const char*)&(MMF_Pointer->Name.Names[x].Name), arg.c_str()) == 0) |
||||||
|
{ |
||||||
|
SendCommand(MMF_Pointer->Name.Names[x].Process, com + 1); |
||||||
|
InterlockedExchange(&(MMF_Pointer->Name.ProcessID), 0); |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
InterlockedExchange(&(MMF_Pointer->Name.ProcessID), 0); |
||||||
|
m_AshitaCore->GetChatManager()->Write("MultiSend: Character not found."); |
||||||
|
} |
||||||
|
|
||||||
|
if (_stricmp(arg.c_str(), "sendgroup") == 0) |
||||||
|
{ |
||||||
|
if (strlen(com) < 3) |
||||||
|
{ |
||||||
|
m_AshitaCore->GetChatManager()->Write("MultiSend: Invalid command."); |
||||||
|
return true; |
||||||
|
} |
||||||
|
com++; |
||||||
|
com += GetArg(com, &arg); |
||||||
|
|
||||||
|
for (std::map<uint16_t, string>::iterator it = m_Group.Map.begin(); it != m_Group.Map.end(); it++) |
||||||
|
{ |
||||||
|
if (_stricmp((*it).second.c_str(), arg.c_str()) == 0) |
||||||
|
{ |
||||||
|
SendCommand(0x8FFF0000 + (*it).first, com + 1); |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
m_AshitaCore->GetChatManager()->Write("MultiSend: Group not found."); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
if (_stricmp(arg.c_str(), "followme") == 0) |
||||||
|
{ |
||||||
|
if (strlen(com) < 2) return true; |
||||||
|
com++; |
||||||
|
com += GetArg(com, &arg); |
||||||
|
|
||||||
|
if (_stricmp(arg.c_str(), "on") == 0) |
||||||
|
{ |
||||||
|
InterlockedExchange(&(MMF_Pointer->Follow.FollowID), GetCurrentProcessId()); |
||||||
|
if (!Zoning) |
||||||
|
{ |
||||||
|
Following = false; |
||||||
|
uint16_t myindex = m_AshitaCore->GetDataManager()->GetParty()->GetMemberTargetIndex(0); |
||||||
|
uint32_t mPosX = (uint32_t)floor(m_AshitaCore->GetDataManager()->GetEntity()->GetLocalX(myindex) * 100); |
||||||
|
uint32_t mPosZ = (uint32_t)floor(m_AshitaCore->GetDataManager()->GetEntity()->GetLocalZ(myindex) * 100); |
||||||
|
MMF_Pointer->Follow.PosX = mPosX; |
||||||
|
MMF_Pointer->Follow.PosZ = mPosZ; |
||||||
|
MMF_Pointer->Follow.Follow = 1; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
Following = false; |
||||||
|
MMF_Pointer->Follow.Follow = 3; |
||||||
|
} |
||||||
|
m_AshitaCore->GetChatManager()->Write("MultiSend: Followme enabled."); |
||||||
|
} |
||||||
|
|
||||||
|
else if (_stricmp(arg.c_str(), "off") == 0) |
||||||
|
{ |
||||||
|
InterlockedExchange(&(MMF_Pointer->Follow.FollowID), 0); |
||||||
|
MMF_Pointer->Follow.Follow = 0; |
||||||
|
m_AshitaCore->GetChatManager()->Write("MultiSend: Followme disabled."); |
||||||
|
} |
||||||
|
|
||||||
|
else |
||||||
|
{ |
||||||
|
m_AshitaCore->GetChatManager()->Writef("MultiSend: Followme is currently %s.", (MMF_Pointer->Follow.FollowID == GetCurrentProcessId()) ? "enabled" : "disabled"); |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
if (_stricmp(arg.c_str(), "follow") == 0) |
||||||
|
{ |
||||||
|
if (strlen(com) < 2) return true; |
||||||
|
com++; |
||||||
|
com += GetArg(com, &arg); |
||||||
|
|
||||||
|
if (_stricmp(arg.c_str(), "on") == 0) |
||||||
|
{ |
||||||
|
FollowEnabled = true; |
||||||
|
m_AshitaCore->GetChatManager()->Write("MultiSend: Follow enabled. Note that if someone is not currently using followme, no movement will occur."); |
||||||
|
} |
||||||
|
|
||||||
|
else if (_stricmp(arg.c_str(), "off") == 0) |
||||||
|
{ |
||||||
|
FollowEnabled = false; |
||||||
|
m_AshitaCore->GetChatManager()->Write("MultiSend: Follow disabled."); |
||||||
|
} |
||||||
|
|
||||||
|
else |
||||||
|
{ |
||||||
|
m_AshitaCore->GetChatManager()->Writef("MultiSend: Follow is currently %s.", FollowEnabled ? "enabled" : "disabled"); |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
if (_stricmp(arg.c_str(), "ignoreself") == 0) |
||||||
|
{ |
||||||
|
if (strlen(com) < 2) return true; |
||||||
|
com++; |
||||||
|
com += GetArg(com, &arg); |
||||||
|
|
||||||
|
if (_stricmp(arg.c_str(), "on") == 0) |
||||||
|
{ |
||||||
|
IgnoreSelf = true; |
||||||
|
m_AshitaCore->GetChatManager()->Write("MultiSend: Now ignoring self-published commands."); |
||||||
|
} |
||||||
|
|
||||||
|
else if (_stricmp(arg.c_str(), "off") == 0) |
||||||
|
{ |
||||||
|
IgnoreSelf = false; |
||||||
|
m_AshitaCore->GetChatManager()->Write("MultiSend: Now accepting self-published commands."); |
||||||
|
} |
||||||
|
|
||||||
|
else |
||||||
|
{ |
||||||
|
m_AshitaCore->GetChatManager()->Writef("MultiSend: Ignore self is currently %s.", IgnoreSelf ? "enabled" : "disabled"); |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
if (_stricmp(arg.c_str(), "debug") == 0) |
||||||
|
{ |
||||||
|
if (strlen(com) < 2) return true; |
||||||
|
com++; |
||||||
|
com += GetArg(com, &arg); |
||||||
|
|
||||||
|
if (_stricmp(arg.c_str(), "on") == 0) |
||||||
|
{ |
||||||
|
IgnoreSelf = true; |
||||||
|
m_AshitaCore->GetChatManager()->Write("MultiSend: Debug prints enabled."); |
||||||
|
} |
||||||
|
|
||||||
|
else if (_stricmp(arg.c_str(), "off") == 0) |
||||||
|
{ |
||||||
|
IgnoreSelf = false; |
||||||
|
m_AshitaCore->GetChatManager()->Write("MultiSend: Debug prints disabled."); |
||||||
|
} |
||||||
|
|
||||||
|
else |
||||||
|
{ |
||||||
|
m_AshitaCore->GetChatManager()->Writef("MultiSend: Debug prints currently %s.", _Debug ? "enabled" : "disabled"); |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
if (_stricmp(arg.c_str(), "reload") == 0) |
||||||
|
{ |
||||||
|
LoadGroups(); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
if (_stricmp(arg.c_str(), "help") == 0) |
||||||
|
{ |
||||||
|
m_AshitaCore->GetChatManager()->Write("Multisend Command Listing"); |
||||||
|
m_AshitaCore->GetChatManager()->Write("/ms send [command] - Sends [command] to all characters with multisend loaded."); |
||||||
|
m_AshitaCore->GetChatManager()->Write("/ms sendto [char name] [command] - Sends [command] to all characters with multisend loaded named [char name]."); |
||||||
|
m_AshitaCore->GetChatManager()->Write("/ms sendgroup [group name] [command] - Sends [command] to all characters with multisend loaded that are defined within the group [group name]."); |
||||||
|
m_AshitaCore->GetChatManager()->Write("/ms follow on/off - When enabled, the current character will obey followme."); |
||||||
|
m_AshitaCore->GetChatManager()->Write("/ms followme on/off - When enabled, all characters with follow enabled will follow this character. Only one character can have this active at a time, if you activate it on a second the first will deactivate automatically."); |
||||||
|
m_AshitaCore->GetChatManager()->Write("/ms reload - Reloads group file without reloading MultiSend."); |
||||||
|
m_AshitaCore->GetChatManager()->Write("/ms ignoreself on/off - When enabled, send and sendgroup commands sent by this character will not execute on this character."); |
||||||
|
m_AshitaCore->GetChatManager()->Write("/ms debug on/off - When enabled, debug prints will be visible."); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t MultiSend::GetArg(const char* text, std::string* buffer) |
||||||
|
{ |
||||||
|
std::string working(text); |
||||||
|
|
||||||
|
if (working[0] == '"') |
||||||
|
{ |
||||||
|
size_t second = working.substr(1).find_first_of('"'); |
||||||
|
if (second != string::npos) |
||||||
|
{ |
||||||
|
*buffer = working.substr(1, second); |
||||||
|
return second + 1; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
size_t space = working.find_first_of(' '); |
||||||
|
if (space != string::npos) |
||||||
|
{ |
||||||
|
*buffer = working.substr(0, space); |
||||||
|
return space; |
||||||
|
} |
||||||
|
|
||||||
|
*buffer = string(text); |
||||||
|
return strlen(text); |
||||||
|
} |
@ -0,0 +1,5 @@ |
|||||||
|
LIBRARY "MultiSend" |
||||||
|
EXPORTS |
||||||
|
GetInterfaceVersion |
||||||
|
CreatePluginInfo |
||||||
|
CreatePlugin |
@ -0,0 +1,43 @@ |
|||||||
|
#include "MultiSend.h" |
||||||
|
|
||||||
|
bool MultiSend::Direct3DInitialize(IDirect3DDevice8* lpDevice) |
||||||
|
{ |
||||||
|
this->m_Direct3DDevice = lpDevice; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Direct3D prerender call to allow this plugin to prepare for rendering. |
||||||
|
* |
||||||
|
* @note This will only be called if you returned true in Direct3DInitialize! |
||||||
|
*/ |
||||||
|
void MultiSend::Direct3DPreRender(void) |
||||||
|
{ |
||||||
|
if (FollowEnabled) |
||||||
|
{ |
||||||
|
if (Following) |
||||||
|
{ |
||||||
|
uint16_t myindex = m_AshitaCore->GetDataManager()->GetParty()->GetMemberTargetIndex(0); |
||||||
|
float MyX = m_AshitaCore->GetDataManager()->GetEntity()->GetLocalX(myindex); |
||||||
|
float MyZ = m_AshitaCore->GetDataManager()->GetEntity()->GetLocalZ(myindex); |
||||||
|
double Distance = sqrt(pow(PositionX - MyX, 2) + pow(PositionZ - MyZ, 2)); |
||||||
|
|
||||||
|
if (Distance > 0.4f) |
||||||
|
{ |
||||||
|
StructPointer->DirX = PositionX - MyX; |
||||||
|
StructPointer->DirY = 0; |
||||||
|
StructPointer->DirZ = PositionZ - MyZ; |
||||||
|
StructPointer->Autorun = 1; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
StructPointer->Autorun = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
else if (StopFollow) |
||||||
|
{ |
||||||
|
StructPointer->Autorun = 0; |
||||||
|
StopFollow = false; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,126 @@ |
|||||||
|
#include "MultiSend.h" |
||||||
|
#include <fstream> |
||||||
|
#include <iostream> |
||||||
|
using namespace rapidxml; |
||||||
|
|
||||||
|
bool MultiSend::MatchID(uint32_t ID) |
||||||
|
{ |
||||||
|
if ((ID & 0xFFFF0000) == 0xFFFF0000) |
||||||
|
{ |
||||||
|
return true; |
||||||
|
} |
||||||
|
if ((ID & 0x8FFF0000) == 0x8FFF0000) |
||||||
|
{ |
||||||
|
uint16_t GroupID = (ID & 0x0000FFFF); |
||||||
|
return (std::find(Groups.begin(), Groups.end(), GroupID) != Groups.end()); |
||||||
|
} |
||||||
|
return (::GetCurrentProcessId() == ID); |
||||||
|
} |
||||||
|
|
||||||
|
void MultiSend::LoadGroups() |
||||||
|
{ |
||||||
|
m_Group.Map.clear(); |
||||||
|
Groups.clear(); |
||||||
|
string Path = m_AshitaCore->GetAshitaInstallPathA(); |
||||||
|
Path += "config\\MultiSend.xml"; |
||||||
|
|
||||||
|
std::ifstream Reader(Path, ios::in | ios::binary | ios::ate); |
||||||
|
if (Reader.is_open()) |
||||||
|
{ |
||||||
|
Reader.seekg(0, ios::end); |
||||||
|
uint32_t Size = (uint32_t)Reader.tellg(); |
||||||
|
|
||||||
|
m_Group.rawdata = new char[Size + 1]; |
||||||
|
Reader.seekg(0, ios::beg); |
||||||
|
Reader.read(m_Group.rawdata, Size); |
||||||
|
Reader.close(); |
||||||
|
m_Group.rawdata[Size] = '\0'; |
||||||
|
try |
||||||
|
{ |
||||||
|
m_Group.doc.parse<0>(m_Group.rawdata); |
||||||
|
m_AshitaCore->GetChatManager()->Write("MultiSend: Groups loaded."); |
||||||
|
m_Group.Loaded = true; |
||||||
|
} |
||||||
|
catch (...) |
||||||
|
{ |
||||||
|
m_AshitaCore->GetChatManager()->Write("MultiSend: Groups failed to parse."); |
||||||
|
m_Group.doc.clear(); |
||||||
|
m_Group.Loaded = false; |
||||||
|
delete m_Group.rawdata; |
||||||
|
} |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
if (m_Group.Loaded) |
||||||
|
{ |
||||||
|
m_Group.doc.clear(); |
||||||
|
delete m_Group.rawdata; |
||||||
|
m_Group.Loaded = false; |
||||||
|
} |
||||||
|
m_AshitaCore->GetChatManager()->Write("MultiSend: No groups file found."); |
||||||
|
} |
||||||
|
|
||||||
|
if (m_Group.Loaded) |
||||||
|
{ |
||||||
|
ReadXML(); |
||||||
|
MatchGroups(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void MultiSend::ReadXML() |
||||||
|
{ |
||||||
|
m_Group.Map.clear(); |
||||||
|
|
||||||
|
xml_node<> *Node = m_Group.doc.first_node(); |
||||||
|
if (_stricmp(Node->name(), "multisend") == 0) Node = Node->first_node(); |
||||||
|
while (Node) |
||||||
|
{ |
||||||
|
if (_stricmp(Node->name(), "group") == 0) |
||||||
|
{ |
||||||
|
xml_attribute<> *Attr = Node->first_attribute("index"); |
||||||
|
if (Attr) |
||||||
|
{ |
||||||
|
xml_attribute<> *Attr2 = Node->first_attribute("name"); |
||||||
|
if (Attr2) |
||||||
|
{ |
||||||
|
m_Group.Map.insert(std::make_pair((uint16_t)atoi(Attr->value()), std::string(Attr2->value()))); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
Node = Node->next_sibling(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void MultiSend::MatchGroups() |
||||||
|
{ |
||||||
|
Groups.clear(); |
||||||
|
if (CurrentName.length() < 3) return; |
||||||
|
if (!m_Group.Loaded) return; |
||||||
|
|
||||||
|
xml_node<> *Node = m_Group.doc.first_node(); |
||||||
|
if (_stricmp(Node->name(), "multisend") == 0) Node = Node->first_node(); |
||||||
|
while (Node) |
||||||
|
{ |
||||||
|
if (_stricmp(Node->name(), "group") == 0) |
||||||
|
{ |
||||||
|
xml_attribute<> *Attr = Node->first_attribute("index"); |
||||||
|
if (Attr) |
||||||
|
{ |
||||||
|
xml_attribute<> *Attr2 = Node->first_attribute("name"); |
||||||
|
if (Attr2) |
||||||
|
{ |
||||||
|
uint16_t Index = (uint16_t)atoi(Attr->value()); |
||||||
|
for (xml_node<> *SubNode = Node->first_node("char"); SubNode != NULL; SubNode = SubNode->next_sibling("char")) |
||||||
|
{ |
||||||
|
if (_stricmp(CurrentName.c_str(), SubNode->value()) == 0) |
||||||
|
{ |
||||||
|
Groups.push_back(Index); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
Node = Node->next_sibling(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,147 @@ |
|||||||
|
#include "MultiSend.h" |
||||||
|
|
||||||
|
bool MultiSend::ReadCommand() |
||||||
|
{ |
||||||
|
if (MMF_Pointer->Command.Command[Position].Active) |
||||||
|
{ |
||||||
|
if (MatchID(MMF_Pointer->Command.Command[Position].Targets)) |
||||||
|
{ |
||||||
|
if ((!IgnoreSelf) |
||||||
|
|| (GetCurrentProcessId() != MMF_Pointer->Command.Command[Position].SendProcess)) |
||||||
|
{ |
||||||
|
const char* text = new char[248]; |
||||||
|
memset((void*)text, 0, 248); |
||||||
|
memcpy((void*)text, &(MMF_Pointer->Command.Command[Position].Command), 248); |
||||||
|
if (_Debug) |
||||||
|
{ |
||||||
|
m_AshitaCore->GetChatManager()->Writef("Sending command: %s", text); |
||||||
|
} |
||||||
|
m_AshitaCore->GetChatManager()->QueueCommand(text, 0); |
||||||
|
delete text; |
||||||
|
} |
||||||
|
} |
||||||
|
Position++; |
||||||
|
if (Position == 100) Position = 0; |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
void MultiSend::SetFollow(bool Active) |
||||||
|
{ |
||||||
|
if (Active) |
||||||
|
{ |
||||||
|
Following = false; |
||||||
|
uint16_t myindex = m_AshitaCore->GetDataManager()->GetParty()->GetMemberTargetIndex(0); |
||||||
|
uint32_t mPosX = (uint32_t)floor(m_AshitaCore->GetDataManager()->GetEntity()->GetLocalX(myindex) * 100); |
||||||
|
uint32_t mPosZ = (uint32_t)floor(m_AshitaCore->GetDataManager()->GetEntity()->GetLocalZ(myindex) * 100); |
||||||
|
MMF_Pointer->Follow.PosX = mPosX; |
||||||
|
MMF_Pointer->Follow.PosZ = mPosZ; |
||||||
|
MMF_Pointer->Follow.Follow = 1; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
MMF_Pointer->Follow.Follow = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void MultiSend::SendCommand(uint32_t ID, const char* Command) |
||||||
|
{ |
||||||
|
char* Text = new char[248]; |
||||||
|
memset((void*)Text, 0, 248); |
||||||
|
memcpy(Text, Command, strlen(Command)); |
||||||
|
SubValues(Text); |
||||||
|
|
||||||
|
Claim(&(MMF_Pointer->Command.ProcessID), 0); |
||||||
|
|
||||||
|
int NextPosition = MMF_Pointer->Command.Position + 1; |
||||||
|
if (NextPosition == 100) NextPosition = 0; |
||||||
|
|
||||||
|
MMF_Pointer->Command.Command[NextPosition].Active = false; |
||||||
|
|
||||||
|
memset(&(MMF_Pointer->Command.Command[MMF_Pointer->Command.Position].Command), 0, 248); |
||||||
|
if (strlen(Text) > 247) |
||||||
|
{ |
||||||
|
memcpy(&(MMF_Pointer->Command.Command[MMF_Pointer->Command.Position].Command), Text, 247); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
memcpy(&(MMF_Pointer->Command.Command[MMF_Pointer->Command.Position].Command), Text, strlen(Text)); |
||||||
|
} |
||||||
|
if (_Debug) |
||||||
|
{ |
||||||
|
m_AshitaCore->GetChatManager()->Writef("Publishing position %d : %s", MMF_Pointer->Command.Position, Text); |
||||||
|
} |
||||||
|
MMF_Pointer->Command.Command[MMF_Pointer->Command.Position].SendProcess = GetCurrentProcessId(); |
||||||
|
MMF_Pointer->Command.Command[MMF_Pointer->Command.Position].Targets = ID; |
||||||
|
MMF_Pointer->Command.Command[MMF_Pointer->Command.Position].Active = true; |
||||||
|
MMF_Pointer->Command.Position = NextPosition; |
||||||
|
InterlockedExchange(&(MMF_Pointer->Command.ProcessID), 0); |
||||||
|
delete Text; |
||||||
|
} |
||||||
|
|
||||||
|
void MultiSend::UpdateName(std::string Name) |
||||||
|
{ |
||||||
|
Claim(&(MMF_Pointer->Name.ProcessID), 0xFFFF0000); |
||||||
|
bool Written = false; |
||||||
|
for (int x = 0; x < 100; x++) |
||||||
|
{ |
||||||
|
if (!MMF_Pointer->Name.Names[x].Active) |
||||||
|
{ |
||||||
|
if (!Written) |
||||||
|
{ |
||||||
|
memcpy(&(MMF_Pointer->Name.Names[x].Name), Name.c_str(), strlen(Name.c_str()) + 1); |
||||||
|
MMF_Pointer->Name.Names[x].Process = GetCurrentProcessId(); |
||||||
|
MMF_Pointer->Name.Names[x].Active = 1; |
||||||
|
Written = true; |
||||||
|
} |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
else if (MMF_Pointer->Name.Names[x].Process == GetCurrentProcessId()) |
||||||
|
{ |
||||||
|
memset(&(MMF_Pointer->Name.Names[x]), 0, sizeof(MMF_Name_Single)); |
||||||
|
if (!Written) |
||||||
|
{ |
||||||
|
memcpy(&(MMF_Pointer->Name.Names[x].Name), Name.c_str(), strlen(Name.c_str()) + 1); |
||||||
|
MMF_Pointer->Name.Names[x].Process = GetCurrentProcessId(); |
||||||
|
MMF_Pointer->Name.Names[x].Active = 1; |
||||||
|
Written = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
else if (strcmp((const char*)&(MMF_Pointer->Name.Names[x].Name), Name.c_str()) == 0) |
||||||
|
{ |
||||||
|
memset(&(MMF_Pointer->Name.Names[x]), 0, sizeof(MMF_Name_Single)); |
||||||
|
if (!Written) |
||||||
|
{ |
||||||
|
memcpy(&(MMF_Pointer->Name.Names[x].Name), Name.c_str(), strlen(Name.c_str()) + 1); |
||||||
|
MMF_Pointer->Name.Names[x].Process = GetCurrentProcessId(); |
||||||
|
MMF_Pointer->Name.Names[x].Active = 1; |
||||||
|
Written = true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
CurrentName = Name; |
||||||
|
MatchGroups(); |
||||||
|
InterlockedExchange(&(MMF_Pointer->Name.ProcessID), 0); |
||||||
|
} |
||||||
|
|
||||||
|
void MultiSend::Claim(uint32_t* Target, uint32_t Mod) |
||||||
|
{ |
||||||
|
uint32_t val = GetCurrentProcessId() + Mod; |
||||||
|
int FailCount = 0; |
||||||
|
InterlockedCompareExchange(Target, val, 0); |
||||||
|
|
||||||
|
while (*Target != val) |
||||||
|
{ |
||||||
|
FailCount++; |
||||||
|
if (FailCount == 50) |
||||||
|
{ |
||||||
|
InterlockedExchange(Target, val); |
||||||
|
return; |
||||||
|
} |
||||||
|
::Sleep(1); |
||||||
|
InterlockedCompareExchange(Target, val, 0); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,126 @@ |
|||||||
|
#include "MultiSend.h" |
||||||
|
#pragma comment(lib, "psapi.lib") |
||||||
|
#include <psapi.h> |
||||||
|
#include <stdint.h> |
||||||
|
|
||||||
|
MultiSend::MultiSend(void) |
||||||
|
: m_AshitaCore(NULL) |
||||||
|
, m_PluginId(0) |
||||||
|
, m_Direct3DDevice(NULL) |
||||||
|
{ } |
||||||
|
MultiSend::~MultiSend(void) |
||||||
|
{ } |
||||||
|
|
||||||
|
plugininfo_t* g_PluginInfo = NULL; |
||||||
|
|
||||||
|
plugininfo_t MultiSend::GetPluginInfo(void) |
||||||
|
{ |
||||||
|
return (*g_PluginInfo); |
||||||
|
} |
||||||
|
|
||||||
|
bool MultiSend::Initialize(IAshitaCore* core, ILogManager* log, uint32_t id) |
||||||
|
{ |
||||||
|
this->m_AshitaCore = core; |
||||||
|
this->m_PluginId = id; |
||||||
|
this->m_LogManager = log; |
||||||
|
|
||||||
|
LoadGroups(); |
||||||
|
|
||||||
|
MODULEINFO mod = { 0 }; |
||||||
|
if (!::GetModuleInformation(::GetCurrentProcess(), ::GetModuleHandle("FFXiMain.dll"), &mod, sizeof(MODULEINFO))) |
||||||
|
return false; |
||||||
|
|
||||||
|
unsigned char* Pointer = (unsigned char*)Ashita::Memory::FindPattern((uintptr_t)mod.lpBaseOfDll, (uintptr_t)mod.SizeOfImage, |
||||||
|
"8BCFE8????FFFF8B0D????????E8????????8BE885ED750CB9", |
||||||
|
0, 0); |
||||||
|
if (Pointer == NULL) return false; |
||||||
|
|
||||||
|
Pointer += 25; |
||||||
|
StructPointer = (sFollow*)(*((DWORD*)Pointer)); |
||||||
|
|
||||||
|
//create a handle to the MMF, size matches the struct we're using for it
|
||||||
|
HANDLE hMapFile = CreateFileMapping( |
||||||
|
INVALID_HANDLE_VALUE, |
||||||
|
NULL, |
||||||
|
PAGE_READWRITE, |
||||||
|
0, // maximum object size (high-order DWORD)
|
||||||
|
sizeof(MMF_Global), // maximum object size (low-order DWORD)
|
||||||
|
"FFXI_MultiSend"); |
||||||
|
|
||||||
|
bool made = (GetLastError() == 0); |
||||||
|
|
||||||
|
if (hMapFile == NULL) |
||||||
|
{ |
||||||
|
throw exception("Could not open or create MMF."); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
MMF_Pointer = (MMF_Global*)MapViewOfFile(hMapFile, // handle to map object
|
||||||
|
FILE_MAP_ALL_ACCESS, // read/write permission
|
||||||
|
0, |
||||||
|
0, |
||||||
|
sizeof(MMF_Global)); |
||||||
|
if (MMF_Pointer == NULL) |
||||||
|
{ |
||||||
|
CloseHandle(hMapFile); |
||||||
|
throw exception("Could not map MMF."); |
||||||
|
} |
||||||
|
|
||||||
|
if (made) |
||||||
|
{ |
||||||
|
memset(MMF_Pointer, 0, sizeof(MMF_Global)); |
||||||
|
} |
||||||
|
Position = MMF_Pointer->Command.Position; |
||||||
|
|
||||||
|
FollowEnabled = true; |
||||||
|
ZoneExtra = false; |
||||||
|
IgnoreSelf = false; |
||||||
|
|
||||||
|
CurrentName = ""; |
||||||
|
if (m_AshitaCore->GetDataManager()->GetParty()->GetMemberActive(0)) |
||||||
|
{ |
||||||
|
uint16_t myindex = m_AshitaCore->GetDataManager()->GetParty()->GetMemberTargetIndex(0); |
||||||
|
UpdateName(std::string(m_AshitaCore->GetDataManager()->GetEntity()->GetName(myindex))); |
||||||
|
} |
||||||
|
|
||||||
|
_Debug = false; |
||||||
|
|
||||||
|
this->Start(); |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
void MultiSend::Release(void) |
||||||
|
{ |
||||||
|
this->Stop(); |
||||||
|
if (MMF_Pointer) |
||||||
|
{ |
||||||
|
UnmapViewOfFile((LPCVOID)MMF_Pointer); |
||||||
|
} |
||||||
|
if (hMapFile) |
||||||
|
{ |
||||||
|
CloseHandle(hMapFile); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
__declspec(dllexport) double __stdcall GetInterfaceVersion(void) |
||||||
|
{ |
||||||
|
return ASHITA_INTERFACE_VERSION; |
||||||
|
} |
||||||
|
|
||||||
|
__declspec(dllexport) void __stdcall CreatePluginInfo(plugininfo_t* lpBuffer) |
||||||
|
{ |
||||||
|
g_PluginInfo = lpBuffer; |
||||||
|
|
||||||
|
strcpy_s(g_PluginInfo->Name, sizeof(g_PluginInfo->Name), "MultiSend"); |
||||||
|
strcpy_s(g_PluginInfo->Author, sizeof(g_PluginInfo->Author), "Thorny"); |
||||||
|
|
||||||
|
g_PluginInfo->InterfaceVersion = ASHITA_INTERFACE_VERSION; |
||||||
|
g_PluginInfo->PluginVersion = 1.00f; |
||||||
|
g_PluginInfo->Priority = 0; |
||||||
|
} |
||||||
|
|
||||||
|
__declspec(dllexport) IPlugin* __stdcall CreatePlugin(void) |
||||||
|
{ |
||||||
|
return (IPlugin*)new MultiSend(); |
||||||
|
} |
@ -0,0 +1,109 @@ |
|||||||
|
#ifndef __ASHITA_MMF_H_INCLUDED__ |
||||||
|
#define __ASHITA_MMF_H_INCLUDED__ |
||||||
|
|
||||||
|
#if defined (_MSC_VER) && (_MSC_VER >= 1020) |
||||||
|
#pragma once |
||||||
|
#endif |
||||||
|
#define SAFE_DELETE(p) if(p) { delete p; p = NULL; } |
||||||
|
|
||||||
|
#include "C:\Ashita 3\Plugins\ADK\Ashita.h" |
||||||
|
#include "Structs.h" |
||||||
|
#include "..\..\pluginheaders\Utilities.h" |
||||||
|
#include "..\..\pluginheaders\rapidxml.hpp" |
||||||
|
#include <time.h> |
||||||
|
#include <list> |
||||||
|
#include <map> |
||||||
|
|
||||||
|
using namespace std; |
||||||
|
|
||||||
|
|
||||||
|
extern plugininfo_t* g_PluginInfo; |
||||||
|
|
||||||
|
struct GroupData |
||||||
|
{ |
||||||
|
bool Loaded; |
||||||
|
char* rawdata; |
||||||
|
rapidxml::xml_document<> doc; |
||||||
|
std::map<uint16_t, string> Map; |
||||||
|
}; |
||||||
|
|
||||||
|
struct sFollow |
||||||
|
{ // 42 bytes
|
||||||
|
unsigned int _unknown_0_4; // 0- 3 Looks like a pointer
|
||||||
|
unsigned short TargetIndex; // 4- 5+2
|
||||||
|
unsigned int TargetID; // 8-11
|
||||||
|
float DirX; // 12-15
|
||||||
|
float DirY; // 16-19
|
||||||
|
float DirZ; // 20-23
|
||||||
|
unsigned char _unknown_24_27[4]; // 24-27
|
||||||
|
unsigned int _unknown_28_31; // 28-31 Same as _unknown_0_4
|
||||||
|
unsigned int FollowIndex; // 32-33+2
|
||||||
|
unsigned int FollowID; // 36-39 Once set will overwrite DirX, DirZ and DirY with directional values
|
||||||
|
unsigned char _zero_40_40; // 40-40
|
||||||
|
unsigned char Autorun; // 41-41
|
||||||
|
}; |
||||||
|
|
||||||
|
class MultiSend : IPlugin, Ashita::Threading::AS_Thread |
||||||
|
{ |
||||||
|
IAshitaCore* m_AshitaCore; |
||||||
|
ILogManager* m_LogManager; |
||||||
|
DWORD m_PluginId; |
||||||
|
IDirect3DDevice8* m_Direct3DDevice; |
||||||
|
sFollow* StructPointer; |
||||||
|
GroupData m_Group; |
||||||
|
|
||||||
|
HANDLE hMapFile; |
||||||
|
MMF_Global* MMF_Pointer; |
||||||
|
|
||||||
|
uint32_t Position; |
||||||
|
volatile bool ZoneExtra; |
||||||
|
volatile bool Following; |
||||||
|
volatile float PositionX; |
||||||
|
volatile float PositionZ; |
||||||
|
std::list<uint16_t> Groups; |
||||||
|
|
||||||
|
std::string CurrentName; |
||||||
|
|
||||||
|
uint32_t Zoning; //0 = not zoning, 1 = waiting to zone, 2 = zoned and waiting to finish
|
||||||
|
volatile bool FollowEnabled; |
||||||
|
volatile bool StopFollow; |
||||||
|
volatile bool IgnoreSelf; |
||||||
|
|
||||||
|
volatile bool _Debug; |
||||||
|
|
||||||
|
public: |
||||||
|
MultiSend(void); |
||||||
|
virtual ~MultiSend(void); |
||||||
|
|
||||||
|
public: |
||||||
|
plugininfo_t GetPluginInfo(void); |
||||||
|
|
||||||
|
public: |
||||||
|
bool Initialize(IAshitaCore* core, ILogManager* log, uint32_t id); |
||||||
|
void Release(void); |
||||||
|
bool Direct3DInitialize(IDirect3DDevice8* lpDevice); |
||||||
|
void Direct3DPreRender(void); |
||||||
|
bool HandleIncomingPacket(uint16_t id, uint32_t size, void* data, void* modified, bool blocked) override; |
||||||
|
bool HandleCommand(const char* command, int32_t type); |
||||||
|
uint32_t ThreadEntry(void) override; |
||||||
|
|
||||||
|
bool MatchID(uint32_t ID); |
||||||
|
bool ReadCommand(); |
||||||
|
void SetFollow(bool Active); |
||||||
|
void SendCommand(uint32_t ID, const char* Command); |
||||||
|
void SubValues(char* Input); |
||||||
|
void UpdateName(std::string Name); |
||||||
|
void Claim(uint32_t* Target, uint32_t Mod); |
||||||
|
|
||||||
|
void LoadGroups(); |
||||||
|
void ReadXML(); |
||||||
|
void MatchGroups(); |
||||||
|
|
||||||
|
uint32_t GetArg(const char* text, std::string* buffer); |
||||||
|
}; |
||||||
|
|
||||||
|
__declspec(dllexport) double __stdcall GetInterfaceVersion(void); |
||||||
|
__declspec(dllexport) void __stdcall CreatePluginInfo(plugininfo_t* lpBuffer); |
||||||
|
__declspec(dllexport) IPlugin* __stdcall CreatePlugin(void); |
||||||
|
|
||||||
|
#endif // __ASHITA_MMF_H_INCLUDED__
|
@ -0,0 +1,19 @@ |
|||||||
|
#include "MultiSend.h" |
||||||
|
|
||||||
|
|
||||||
|
bool MultiSend::HandleIncomingPacket(uint16_t id, uint32_t size, void* data, void* modified, bool blocked) |
||||||
|
{ |
||||||
|
if (id == 0x00A) |
||||||
|
{ |
||||||
|
if (strcmp(CurrentName.c_str(), (const char*)data + 0x84)) |
||||||
|
{ |
||||||
|
UpdateName(std::string((const char*)data + 0x84)); |
||||||
|
} |
||||||
|
} |
||||||
|
if (id == 0x00B) |
||||||
|
{ |
||||||
|
Zoning = 1; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
@ -0,0 +1,49 @@ |
|||||||
|
#ifndef _STRUCTS_H_ |
||||||
|
#define _STRUCTS_H_ |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
|
||||||
|
struct MMF_Name_Single |
||||||
|
{ |
||||||
|
uint32_t Process; |
||||||
|
uint8_t Name[64]; |
||||||
|
uint8_t Active; |
||||||
|
}; |
||||||
|
|
||||||
|
struct MMF_Name |
||||||
|
{ |
||||||
|
uint32_t ProcessID; |
||||||
|
MMF_Name_Single Names[100]; |
||||||
|
}; |
||||||
|
|
||||||
|
struct MMF_ICommand_Single |
||||||
|
{ |
||||||
|
uint8_t Active; |
||||||
|
uint32_t Targets; |
||||||
|
uint32_t SendProcess; |
||||||
|
uint8_t Command[248]; |
||||||
|
}; |
||||||
|
|
||||||
|
struct MMF_ICommand |
||||||
|
{ |
||||||
|
uint32_t ProcessID; |
||||||
|
uint32_t Position; |
||||||
|
MMF_ICommand_Single Command[100]; |
||||||
|
}; |
||||||
|
|
||||||
|
struct MMF_IFollow |
||||||
|
{ |
||||||
|
uint16_t Zone; |
||||||
|
uint32_t FollowID; |
||||||
|
uint32_t Follow; |
||||||
|
int32_t PosX; |
||||||
|
int32_t PosZ; |
||||||
|
}; |
||||||
|
|
||||||
|
struct MMF_Global |
||||||
|
{ |
||||||
|
MMF_Name Name; |
||||||
|
MMF_ICommand Command; |
||||||
|
MMF_IFollow Follow; |
||||||
|
}; |
||||||
|
#endif |
@ -0,0 +1,63 @@ |
|||||||
|
#include "MultiSend.h" |
||||||
|
|
||||||
|
void MultiSend::SubValues(char* Input) |
||||||
|
{ |
||||||
|
std::string Working(Input); |
||||||
|
|
||||||
|
size_t find = Working.find("[me]"); |
||||||
|
if (find != string::npos) |
||||||
|
{ |
||||||
|
Working.replace(find, 4, std::to_string(m_AshitaCore->GetDataManager()->GetParty()->GetMemberServerId(0))); |
||||||
|
} |
||||||
|
|
||||||
|
find = Working.find("[p0]"); |
||||||
|
if (find != string::npos) |
||||||
|
{ |
||||||
|
Working.replace(find, 4, std::to_string(m_AshitaCore->GetDataManager()->GetParty()->GetMemberServerId(0))); |
||||||
|
} |
||||||
|
|
||||||
|
find = Working.find("[p1]"); |
||||||
|
if (find != string::npos) |
||||||
|
{ |
||||||
|
Working.replace(find, 4, std::to_string(m_AshitaCore->GetDataManager()->GetParty()->GetMemberServerId(1))); |
||||||
|
} |
||||||
|
|
||||||
|
find = Working.find("[p2]"); |
||||||
|
if (find != string::npos) |
||||||
|
{ |
||||||
|
Working.replace(find, 4, std::to_string(m_AshitaCore->GetDataManager()->GetParty()->GetMemberServerId(2))); |
||||||
|
} |
||||||
|
|
||||||
|
find = Working.find("[p3]"); |
||||||
|
if (find != string::npos) |
||||||
|
{ |
||||||
|
Working.replace(find, 4, std::to_string(m_AshitaCore->GetDataManager()->GetParty()->GetMemberServerId(3))); |
||||||
|
} |
||||||
|
|
||||||
|
find = Working.find("[p4]"); |
||||||
|
if (find != string::npos) |
||||||
|
{ |
||||||
|
Working.replace(find, 4, std::to_string(m_AshitaCore->GetDataManager()->GetParty()->GetMemberServerId(4))); |
||||||
|
} |
||||||
|
|
||||||
|
find = Working.find("[p5]"); |
||||||
|
if (find != string::npos) |
||||||
|
{ |
||||||
|
Working.replace(find, 4, std::to_string(m_AshitaCore->GetDataManager()->GetParty()->GetMemberServerId(5))); |
||||||
|
} |
||||||
|
|
||||||
|
find = Working.find("[t]"); |
||||||
|
if (find != string::npos) |
||||||
|
{ |
||||||
|
unsigned int TargetIndex = m_AshitaCore->GetDataManager()->GetTarget()->GetTargetIndex(); |
||||||
|
if (m_AshitaCore->GetDataManager()->GetTarget()->GetSubTargetActive()) |
||||||
|
{ |
||||||
|
TargetIndex = m_AshitaCore->GetDataManager()->GetTarget()->GetSubTargetIndex(); |
||||||
|
} |
||||||
|
Working.replace(find, 3, std::to_string(m_AshitaCore->GetDataManager()->GetEntity()->GetServerId(TargetIndex))); |
||||||
|
} |
||||||
|
|
||||||
|
const char* base = Working.c_str(); |
||||||
|
memset(Input, 0, 248); |
||||||
|
memcpy(Input, base, strlen(base)); |
||||||
|
} |
@ -0,0 +1,85 @@ |
|||||||
|
#include "MultiSend.h" |
||||||
|
|
||||||
|
uint32_t MultiSend::ThreadEntry(void) |
||||||
|
{ |
||||||
|
while (!this->IsTerminated()) |
||||||
|
{ |
||||||
|
if (m_AshitaCore->GetDataManager()->GetParty()->GetMemberActive(0)) |
||||||
|
{ |
||||||
|
if (Zoning == 2) Zoning = 0; |
||||||
|
} |
||||||
|
else if (Zoning == 1) |
||||||
|
{ |
||||||
|
Zoning = 2; |
||||||
|
} |
||||||
|
|
||||||
|
if (MMF_Pointer->Follow.Follow) |
||||||
|
{ |
||||||
|
if (((MMF_Global*)MMF_Pointer)->Follow.FollowID == GetCurrentProcessId()) |
||||||
|
{ |
||||||
|
if (!Zoning) |
||||||
|
{ |
||||||
|
Following = false; |
||||||
|
uint16_t myindex = m_AshitaCore->GetDataManager()->GetParty()->GetMemberTargetIndex(0); |
||||||
|
int32_t mPosX = (int32_t)floor(m_AshitaCore->GetDataManager()->GetEntity()->GetLocalX(myindex) * 100); |
||||||
|
int32_t mPosZ = (int32_t)floor(m_AshitaCore->GetDataManager()->GetEntity()->GetLocalZ(myindex) * 100); |
||||||
|
MMF_Pointer->Follow.PosX = mPosX; |
||||||
|
MMF_Pointer->Follow.PosZ = mPosZ; |
||||||
|
MMF_Pointer->Follow.Follow = 1; |
||||||
|
MMF_Pointer->Follow.Zone = m_AshitaCore->GetDataManager()->GetParty()->GetMemberZone(0); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
Following = false; |
||||||
|
MMF_Pointer->Follow.Follow = 2; |
||||||
|
} |
||||||
|
} |
||||||
|
else if (MMF_Pointer->Follow.Follow != 3) |
||||||
|
{ |
||||||
|
if (m_AshitaCore->GetDataManager()->GetParty()->GetMemberActive(0)) |
||||||
|
{ |
||||||
|
if (MMF_Pointer->Follow.Follow == 2) |
||||||
|
{ |
||||||
|
if (!ZoneExtra) |
||||||
|
{ |
||||||
|
Following = false; |
||||||
|
uint16_t myindex = m_AshitaCore->GetDataManager()->GetParty()->GetMemberTargetIndex(0); |
||||||
|
float MyX = m_AshitaCore->GetDataManager()->GetEntity()->GetLocalX(myindex); |
||||||
|
float MyZ = m_AshitaCore->GetDataManager()->GetEntity()->GetLocalZ(myindex); |
||||||
|
PositionX = (float)MMF_Pointer->Follow.PosX / 100.0f; |
||||||
|
PositionZ = (float)MMF_Pointer->Follow.PosZ / 100.0f; |
||||||
|
float Scale = 1.0f + (8.0f / sqrt(pow(PositionX - MyX, 2) + pow(PositionZ - MyZ, 2))); |
||||||
|
PositionX = (PositionX * Scale) + MyX; |
||||||
|
PositionZ = (PositionZ * Scale) + MyZ; |
||||||
|
ZoneExtra = true; |
||||||
|
Following = true; |
||||||
|
} |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
ZoneExtra = false; |
||||||
|
PositionX = (float)MMF_Pointer->Follow.PosX / 100.0f; |
||||||
|
PositionZ = (float)MMF_Pointer->Follow.PosZ / 100.0f; |
||||||
|
Following = true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
if (Following) StopFollow = true; |
||||||
|
Following = false; |
||||||
|
} |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
if (Following) StopFollow = true; |
||||||
|
Following = false; |
||||||
|
} |
||||||
|
|
||||||
|
while (ReadCommand()) {} |
||||||
|
|
||||||
|
::Sleep(1); |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
Loading…
Reference in new issue