WhatsApp Discord
Main 5.2]MuHelper + AIControl (Full Code) - Source Mu - Mu Server Files
 

Noticias:

SMF - Just Installed!

Menú principal

Main 5.2]MuHelper + AIControl (Full Code)

Publicado por Dakosmu, Jul 03, 2025, 03:06 PM

Tema anterior - Siguiente tema

0 Miembros y 1 Visitante están viendo este tema.

Dakosmu

Configuración y Modificaciones del MuHelper + AIControl (Código Completo)

Introducción

Primero que nada, me gustaría compartir con todos el módulo AIControl que he portado de otra fuente. Después de eso, trabajé en resolver varios problemas para hacerlo compatible con la Base 5.2. A partir de ahora, es relativamente estable. Aún quedan algunos aspectos ocultos que no he descubierto completamente. Si encuentras problemas al usarlo, no dudes en solucionarlos por tu cuenta o compartirlos con la comunidad para obtener soporte. Eso es todo por ahora. Si no puedes agregarlo, o no entiendes cómo integrarlo, no dudes en preguntar y te proporcionaré más ayuda.

Código fuente y descarga

  • Mi contribución: Edición / Adición / Corrección
  • Sven-n: Regístrate para ver el enlace





Guía de Implementación

Ahora bien, ¡comencemos!

1. Modificaciones en _enum.h

Primero, declara el ID de la ventana en enum INTERFACE_LIST dentro de _enum.h:

INTERFACE_MUHELPER,
INTERFACE_MUHELPER_EXT,
INTERFACE_MUHELPER_SKILL_LIST,

2. Modificaciones en NewUISystem.cpp

2.1. Constructor CNewUISystem::CNewUISystem()

En el constructor CNewUISystem::CNewUISystem(), inicializa:

m_pNewUIMuHelper = nullptr;
m_pNewUIMuHelperConfig = nullptr;
m_pNewUIMuHelperSkillList = nullptr;

2.2. Función: bool CNewUISystem::LoadMainSceneInterface()

Inicializa con SetPos:

m_pNewUIMuHelper = new CNewUIMuHelper;
if (m_pNewUIMuHelper->Create(m_pNewUIMng, setPosRight(640 - 190), 0) == false)
return false;

m_pNewUIMuHelperConfig = new CNewUIMuHelperConfig;
if (m_pNewUIMuHelperConfig->Create(m_pNewUIMng, setPosRight(640 - 380), 0) == false)
return false;

m_pNewUIMuHelperSkillList = new CNewUIMuHelperSkillList;
if (m_pNewUIMuHelperSkillList->Create(m_pNewUIMng, m_pNewUI3DRenderMng) == false)
return false;

2.3. Liberar memoria

No olvides liberar la memoria en void CNewUISystem::UnloadMainSceneInterface():

SAFE_DELETE(m_pNewUIMuHelper);
SAFE_DELETE(m_pNewUIMuHelperConfig);
SAFE_DELETE(m_pNewUIMuHelperSkillList);

2.4. Función: void CNewUISystem::Show(DWORD dwKey)

Añade: Cuando se detecta la ventana de Configuración de MuHelper, detiene automáticamente el helper.

else if (dwKey == INTERFACE_MUHELPER)
{
Hide(INTERFACE_INVENTORY);
HideGroupBeforeOpenInterface();
MUHelper::g_MuHelper.Stop();
}

2.5. Función: void CNewUISystem::Hide(DWORD dwKey)

Añade esto al final de la función:

else if (dwKey == INTERFACE_MUHELPER)
{
m_pNewUIMng->ShowInterface(SEASON3B::INTERFACE_MUHELPER_SKILL_LIST, false);
m_pNewUIMng->ShowInterface(SEASON3B::INTERFACE_MUHELPER_EXT, false);
}

2.6. Función: void CNewUISystem::HideAllGroupA()

Añade:

INTERFACE_MUHELPER,
INTERFACE_MUHELPER_EXT,
INTERFACE_MUHELPER_SKILL_LIST,

2.7. Función: void CNewUISystem::HideGroupBeforeOpenInterface()

Añade:

INTERFACE_MUHELPER,
INTERFACE_MUHELPER_EXT,
INTERFACE_MUHELPER_SKILL_LIST,

2.8. Crear accesores usando GetInstance()

CNewUIMuHelper* CNewUISystem::Get_pNewUIMuHelper() const
{
return m_pNewUIMuHelper;
}

CNewUIMuHelperConfig* CNewUISystem::Get_pNewUIMuHelperConfig() const
{
return m_pNewUIMuHelperConfig;
}

CNewUIMuHelperSkillList* CNewUISystem::Get_pNewUIMuHelperSkillList() const
{
return m_pNewUIMuHelperSkillList;
}

3. Modificaciones en NewUISystem.h

Añade:

#include "NewUIMuHelper.h"
#include "NewUIMuHelperConfig.h"
#include

CNewUIMuHelper* m_pNewUIMuHelper;
CNewUIMuHelperConfig* m_pNewUIMuHelperConfig;
CNewUIMuHelperSkillList* m_pNewUIMuHelperSkillList;

///
CNewUIMuHelper* Get_pNewUIMuHelper() const;
CNewUIMuHelperConfig* Get_pNewUIMuHelperConfig() const;
CNewUIMuHelperSkillList* Get_pNewUIMuHelperSkillList() const;

//Inicializar punteros
#define g_pNewUIMuHelper SEASON3B::CNewUISystem::GetInstance()->Get_pNewUIMuHelper()
#define g_pNewUIMuHelperExt SEASON3B::CNewUISystem::GetInstance()->Get_pNewUIMuHelperConfig()
#define g_pNewUIMuHelperSkillList SEASON3B::CNewUISystem::GetInstance()->Get_pNewUIMuHelperSkillList()

4. Modificaciones en UIWindows.h

En UIWindows.h, crea un nuevo ID de temporizador:

#define MUHELPER_TIMER 1005

5. Modificaciones en WinMain.cpp

Ve a WinMain.cpp.
Busca la línea:

SetTimer(g_hWnd, HACK_TIMER, 20 * 1000, NULL);

Añade debajo:

SetTimer(g_hWnd, MUHELPER_TIMER, 250 /* ms */, MUHelper::CMuHelper::TimerProc);

6. Modificaciones en NewUICustomMessageBox.cpp

Continúa en NewUICustomMessageBox.cpp.
Busca la función: CALLBACK_RESULT SEASON3B::CSystemMenuMsgBox::GameOverBtnDown
Añade dentro del bloque else:

MUHelper::g_MuHelper.TriggerStop(); // Añade esto.
LogOut = true;
SendRequestLogOut(0);

También añade en el bloque else de CALLBACK_RESULT SEASON3B::CSystemMenuMsgBox::ChooseServerBtnDown:

MUHelper::g_MuHelper.TriggerStop(); // Añade esto.
g_pNewUIMng->ResetActiveUIObj();
LogOut = true;
SendRequestLogOut(2);

Continúa en CALLBACK_RESULT SEASON3B::CSystemMenuMsgBox::ChooseCharacterBtnDown.
Añade en el bloque else:

MUHelper::g_MuHelper.TriggerStop(); // Añade esto.
g_pNewUIMng->ResetActiveUIObj();
LogOut = true;
SendRequestLogOut(1);

7. Modificaciones en NewUIHeroPositionInfo.h

Luego, en NewUIHeroPositionInfo.h.
Crea un array de botones con 4 elementos dentro de la clase CNewUIHeroPositionInfo:

CNewUIButton m_Btn[4]; // El elemento 0 lo uso para otros propósitos.

8. Modificaciones en NewUIHeroPositionInfo.cpp

Luego, en NewUIHeroPositionInfo.cpp.

8.1. Función: void CNewUIHeroPositionInfo::InitRenderButton()

Si no existe, crea una nueva función y declara los botones dentro:

SetButtonInfo(&m_Btn[1], IMAGE_HERO_POSITION_INFO_BASE_WINDOW + 1, m_Pos.x + WidenX + 21, m_Pos.y + 1, 18, 13, 1, 0, 1);
SetButtonInfo(&m_Btn[2], IMAGE_HERO_POSITION_INFO_BASE_WINDOW + 2, m_Pos.x + WidenX + 37, m_Pos.y + 1, 18, 13, 1, 0, 1);
SetButtonInfo(&m_Btn[3], IMAGE_HERO_POSITION_INFO_BASE_WINDOW + 3, m_Pos.x + WidenX + 37, m_Pos.y + 1, 18, 13, 1, 0, 1);

8.2. Función: bool CNewUIHeroPositionInfo::Render()

Inserta esto:

EnableAlphaTest();
glColor4f(1.f, 1.f, 1.f, 1.f);
m_Btn[1].Render(); // Añade esto
MUHelper::g_MuHelper.IsActive() ? m_Btn[3].Render() : m_Btn[2].Render(); // Añade esto

8.3. Función: bool CNewUIHeroPositionInfo::BtnProcess()

Dentro de: bool CNewUIHeroPositionInfo::BtnProcess()

if (m_Btn[1].UpdateMouseEvent()) // Config
{
g_pNewUISystem->Toggle(SEASON3B::INTERFACE_MUHELPER);
PlayBuffer(SOUND_CLICK01);
return true;
}

// Puedes omitir esta parte si no la tienes. Comprueba manualmente IsVisible para activar Stop
if (m_Btn[2].UpdateMouseEvent()) // Start
{
if (g_pNewUISystem->IsVisible(SEASON3B::INTERFACE_MUHELPER))
{
CMsgBoxIGSCommon* pMsgBox = NULL;
CreateMessageBox(MSGBOX_LAYOUT_CLASS(CMsgBoxIGSCommonLayout), &pMsgBox);
pMsgBox->Initialize(GlobalText[3028], GlobalText[3186]);
return false;
}
else if (CharacterAttribute->Level < CharacterAttribute->MuHelperLevel) // Comprobar nivel. 0 - 80 por SPK
{
unicode::t_char szText[MAX_TEXT_LENGTH] = { '\0', };
sprintf(szText, GlobalText[3188], CharacterAttribute->MuHelperLevel);
CMsgBoxIGSCommon* pMsgBox = NULL;
CreateMessageBox(MSGBOX_LAYOUT_CLASS(CMsgBoxIGSCommonLayout), &pMsgBox);
pMsgBox->Initialize(GlobalText[3028], szText);
return false;
}
else
{
MUHelper::g_MuHelper.Toggle();
}
PlayBuffer(SOUND_CLICK01);
return true;
}

9. Modificaciones de Hotkey

Continúa: Hotkey:

bool SEASON3B::CNewUIHotKey::UpdateKeyEvent()

else if (SEASON3B::IsPress(VK_HOME))
{
//Se puede omitir si no se usa. Esta parte alterna directamente.
if (g_pNewUISystem->IsVisible(SEASON3B::INTERFACE_MUHELPER))
{
CMsgBoxIGSCommon* pMsgBox = NULL;
CreateMessageBox(MSGBOX_LAYOUT_CLASS(CMsgBoxIGSCommonLayout), &pMsgBox);
pMsgBox->Initialize(GlobalText[3028], GlobalText[3186]);
return false;
}
else if (CharacterAttribute->Level < CharacterAttribute->MuHelperLevel)
{
unicode::t_char szText[MAX_TEXT_LENGTH] = { '\0', };
sprintf(szText, GlobalText[3188], CharacterAttribute->MuHelperLevel);
CMsgBoxIGSCommon* pMsgBox = NULL;
CreateMessageBox(MSGBOX_LAYOUT_CLASS(CMsgBoxIGSCommonLayout), &pMsgBox);
pMsgBox->Initialize(GlobalText[3028], szText);
return false;
}
else
{
MUHelper::g_MuHelper.Toggle();
}
return false;
}

10. Crear Render para el Icono de Habilidad

Similar a NewUIMainFrameWindow.h.
La siguiente función ya existe:

void RenderSkillIcon(int iIndex, float x, float y, float width, float height)

Modifícala a:

void RenderSkillIcon(int iIndex, float x, float y, float width, float height, int TypeMuHelper = 0);

Al principio del cuerpo de la función, añade:

void SEASON3B::CNewUISkillList::RenderSkillIcon(int iIndex, float x, float y, float width, float height, int TypeMuHelper)
{
WORD bySkillType = CharacterAttribute->Skill[iIndex];
if (TypeMuHelper == 1) // MuHelper obtiene la lista de habilidades
{
bySkillType = iIndex;
if ((bySkillType == 0 || !gSkillManager.FindHeroSkill((ActionSkillType)bySkillType)))
{
return;
}
}
//....Otros códigos siguen
}

11. Modificaciones en wsclient.cpp

En wsclient.cpp
Encuentra: void ReceiveMoveCharacter(const BYTE* ReceiveBuffer)

Busca:

CHARACTER* c = &CharactersClient[FindCharacterIndex(Key)];
OBJECT* o = &c->Object;

Añade esto:

if (IsMonster(c))
{
MUHelper::g_MuHelper.AddTarget(Key, false);
}

Encuentra: BOOL ReceiveTeleport(const BYTE* ReceiveBuffer, BOOL bEncrypted)

Añade al final de la función:

MUHelper::g_MuHelper.TriggerStop();

Encuentra: void ReceiveCreateMonsterViewport(const BYTE* ReceiveBuffer)

Añade dentro del bucle:

if (IsMonster(c))
{
MUHelper::g_MuHelper.AddTarget(Key, false);
}

Encuentra: void ReceiveAttackDamage(const BYTE* ReceiveBuffer)

Añade debajo del cuerpo de la función:

if (IsMonster(c) || IsPlayer(c))
{
MUHelper::g_MuHelper.AddTarget(Key, true);
}

Encuentra: void ReceiveAction(const BYTE* ReceiveBuffer, int Size)

Añade dentro de case AT_ATTACK2::

c->TargetCharacter = HeroIndex;

if (IsMonster(c) || IsPlayer(c))
{
MUHelper::g_MuHelper.AddTarget(Key, true);
}

Encuentra: void ReceiveDie(const BYTE* ReceiveBuffer, int Size)

Al final de la función:

if (c == Hero)
{
MUHelper::g_MuHelper.TriggerStop();
}

Encuentra: void ReceiveCreateItemViewport(const BYTE* ReceiveBuffer)

Añade esto:

CreateItem(&Items[Key], Data2->Item, Position, CreateFlag);
MUHelper::g_MuHelper.AddItem(Key, { Data2->PositionX, Data2->PositionY });

Encuentra: void ReceiveDeleteItemViewport(const BYTE* ReceiveBuffer)

Añade al final del bucle:

Offset += sizeof(PDELETE_CHARACTER);
MUHelper::g_MuHelper.DeleteItem(Key);

Encuentra: void ReceiveCreateMoney(const BYTE* ReceiveBuffer)

Añade:

CreateMoneyDrop(&Items[Data->Id], Data->Amount, Position, Data->IsFreshDrop);
MUHelper::g_MuHelper.AddItem(Data->Id, { Data->PositionX, Data->PositionY });

Crear 2 nuevas funciones:

void ReceiveMuHelperConfigurationData(const BYTE* ReceiveBuffer)
{
const PRECEIVE_MUHELPER_DATA* pMuHelperData = reinterpret_cast

MUHelper::ConfigData config;
MUHelper::ConfigDataSerDe::Deserialize(pMuHelperData, config);
g_pNewUIMuHelper->LoadSavedConfig(config);
g_ConsoleDebug->Write(MCD_RECEIVE, "0xAE [ReceiveMuHelperConfigurationData]");
}
//------------------------------------------
void ReceiveMuHelperStatusUpdate(const BYTE ReceiveBuffer)
{
if (ReceiveBuffer == nullptr)
return;

const PRECEIVE_MUHELPER_STATUS* pMuHelperStatus = reinterpret_cast<const PRECEIVE_MUHELPER_STATUS*>(ReceiveBuffer + 4);
if (pMuHelperStatus == nullptr)
{
    assert(false);
    return;
}

if (pMuHelperStatus->Pause)
{
    MUHelper::g_MuHelper.Stop();
}
else
{
    MUHelper::g_MuHelper.Start();

    if (pMuHelperStatus->Money > 0 && pMuHelperStatus->ConsumeMoney)
    {
        MUHelper::g_MuHelper.AddCost(pMuHelperStatus->Money);
        int iTotalCost = MUHelper::g_MuHelper.GetTotalCost();

        char Text[100];
        sprintf(Text, GlobalText[3586], iTotalCost);
        g_pChatListBox->AddText("", Text, SEASON3B::TYPE_SYSTEM_MESSAGE);
    }
}

g_ConsoleDebug->Write(MCD_RECEIVE, "0x51 [ReceiveMuHelperStatusUpdate]");
}

Estas 2 funciones envían paquetes al GameServer a través de los puertos AE y BF:51.

12. Modificaciones en TranslateProtocol

Ve a TranslateProtocol.
Busca el paquete: 0xBF

Asigna:

case 0x51: ReceiveMuHelperStatusUpdate(ReceiveBuffer); break;

Al final de la función Protocol añade:

case 0xAE: ReceiveMuHelperConfigurationData(ReceiveBuffer); break; // este paquete es independiente

13. Modificaciones en wsclient.h

Luego, en wsclient.h
Al final del encabezado, / MU Helper:

#pragma pack(push, 1)
typedef struct
{
BYTE DataStartMarker;                    // Index: 4
BYTE : 3; // Unused
BYTE JewelOrGem : 1;                     // Index: 5
BYTE SetItem : 1;
BYTE ExcellentItem : 1;
BYTE Zen : 1;
BYTE AddExtraItem : 1;
BYTE HuntingRange : 4;                   // Index: 6
BYTE ObtainRange : 4;
WORD DistanceMin;                        // Index: 7
WORD BasicSkill1;                        // Index: 9
WORD ActivationSkill1;                   // Index: 11
WORD DelayMinSkill1;                     // Index: 13
WORD ActivationSkill2;                   // Index: 15
WORD DelayMinSkill2;                     // Index: 17
WORD CastingBuffMin;                     // Index: 19
WORD BuffSkill0NumberID;                 // Index: 21
WORD BuffSkill1NumberID;                 // Index: 23
WORD BuffSkill2NumberID;                 // Index: 25
BYTE HPStatusAutoPotion : 4;             // Index: 27
BYTE HPStatusAutoHeal : 4;
BYTE HPStatusOfPartyMembers : 4;         // Index: 28
BYTE HPStatusDrainLife : 4;
BYTE AutoPotion : 1;                     // Index: 29
BYTE AutoHeal : 1;
BYTE DrainLife : 1;
BYTE LongDistanceAttack : 1;
BYTE OriginalPosition : 1;
BYTE Combo : 1;
BYTE Party : 1;
BYTE PreferenceOfPartyHeal : 1;
BYTE BuffDurationforAllPartyMembers : 1; // Index: 30
BYTE UseDarkSpirits : 1;
BYTE BuffDuration : 1;
BYTE Skill1Delay : 1;
BYTE Skill1Con : 1;
BYTE Skill1PreCon : 1;
BYTE Skill1SubCon : 2;
BYTE Skill2Delay : 1;                    // Index: 31
BYTE Skill2Con : 1;
BYTE Skill2PreCon : 1;
BYTE Skill2SubCon : 2;
BYTE RepairItem : 1;
BYTE PickAllNearItems : 1;
BYTE PickSelectedItems : 1;
BYTE PetAttack;                          // Index: 32
BYTE _UnusedPadding[36];
char ExtraItems[12][15];                 // Index: 69
} PRECEIVE_MUHELPER_DATA, * LPRECEIVE_MUHELPER_DATA;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct
{
DWORD ConsumeMoney;
DWORD Money;
DWORD Pause;
} PRECEIVE_MUHELPER_STATUS, * LPRECEIVE_MUHELPER_STATUS;
#pragma pack(pop)

template

return reinterpret_cast<T*>(const_cast<BYTE*>(buffer));
}
struct PMSG_HELPER_START_SEND
{
PSBMSG_HEAD header; // C1:BF:51
BYTE type;
};

14. wsclientinline.h

Añade este macro en wsclientinline.h:

#define SendMuHelperStatusChangeRequest(status)

{

CStreamPacketEngine spe;

spe.Init(0xC1, 0xBF);

spe << (BYTE)0x51;

spe << (BYTE)(status);

spe.Send();

}

#define SendMuHelperSaveDataRequest(data, size)

{

CStreamPacketEngine spe;

spe.Init(0xC1, 0xAE);

spe.AddData(data, size, FALSE);

spe.Send();

}

Bon Dia

Dakosmu

NewUIMuHelper.h

#pragma once

#include <array>
#include <vector>

#include "NewUIBase.h"
#include "NewUIManager.h"
#include "NewUIButton.h"
#include "MUHelper/MuHelper.h"

namespace SEASON3B
{
    class CNewUIMuHelper : public CNewUIObj
    {
    public:
        CNewUIMuHelper();
        ~CNewUIMuHelper();
        void AllDataOfflineSave();
        bool Create(CNewUIManager* pNewUIMng, int x, int y);
        void Release();

        void Show(bool bShow);
        bool Render();
        bool Update();
        bool UpdateMouseEvent();
        bool UpdateKeyEvent();

        float GetLayerDepth();
        float GetKeyEventOrder();
        void AutoReset();

        void Reset();
        void LoadSavedConfig(const MUHelper::ConfigData& config);
        void AssignSkill(int iSkill);
        static int GetIntFromTextInput(char* pstrInput);
        inline void WritePrivateProfileInt(const char* section, const char* key, int value, const char* path);

        enum ESkillSlot
        {
            SUB_PAGE_SKILL2_CONFIG = 2,
            SUB_PAGE_SKILL3_CONFIG,
            SUB_PAGE_POTION_CONFIG_ELF,
            SUB_PAGE_POTION_CONFIG_SUMMY,
            SUB_PAGE_POTION_CONFIG,
            SUB_PAGE_PARTY_CONFIG,
            SUB_PAGE_PARTY_CONFIG_ELF
        };
        enum ECheckBoxId : uint16_t
        {
            CHECKBOX_ID_POTION = 0,
            CHECKBOX_ID_LONG_DISTANCE,
            CHECKBOX_ID_ORIG_POSITION,
            CHECKBOX_ID_SKILL2_DELAY,
            CHECKBOX_ID_SKILL2_CONDITION,
            CHECKBOX_ID_SKILL3_DELAY,
            CHECKBOX_ID_SKILL3_CONDITION,
            CHECKBOX_ID_COMBO,
            CHECKBOX_ID_BUFF_DURATION,
            CHECKBOX_ID_USE_PET,
            CHECKBOX_ID_PARTY,
            CHECKBOX_ID_AUTO_HEAL,
            CHECKBOX_ID_DRAIN_LIFE,
            CHECKBOX_ID_REPAIR_ITEM,
            CHECKBOX_ID_START_OFFLINE,
            CHECKBOX_ID_PICK_ALL,
            CHECKBOX_ID_PICK_SELECTED,
            CHECKBOX_ID_PICK_JEWEL,
            CHECKBOX_ID_PICK_ANCIENT,
            CHECKBOX_ID_PICK_ZEN,
            CHECKBOX_ID_PICK_EXCELLENT,
            CHECKBOX_ID_ADD_OTHER_ITEM,
            CHECKBOX_ID_AUTO_ACCEPT_FRIEND,
            CHECKBOX_ID_AUTO_DEFEND,
            CHECKBOX_ID_AUTO_ACCEPT_GUILD,
            CHECKBOX_ID_DR_ATTACK_CEASE,
            CHECKBOX_ID_DR_ATTACK_AUTO,
            CHECKBOX_ID_DR_ATTACK_TOGETHER,
            CHECKBOX_ID_AUTO_RESET
        };

        enum EButtonId : uint16_t
        {
            BUTTON_ID_HUNT_RANGE_ADD = 0,
            BUTTON_ID_HUNT_RANGE_MINUS,
            BUTTON_ID_SKILL2_CONFIG,
            BUTTON_ID_SKILL3_CONFIG,
            BUTTON_ID_POTION_CONFIG_ELF,
            BUTTON_ID_POTION_CONFIG_SUMMY,
            BUTTON_ID_POTION_CONFIG,
            BUTTON_ID_PARTY_CONFIG,
            BUTTON_ID_PARTY_CONFIG_ELF,
            BUTTON_ID_PICK_RANGE_ADD,
            BUTTON_ID_PICK_RANGE_MINUS,
            BUTTON_ID_ADD_OTHER_ITEM,
            BUTTON_ID_DELETE_OTHER_ITEM,
            BUTTON_ID_SAVE_CONFIG,
            BUTTON_ID_INIT_CONFIG,
            BUTTON_ID_EXIT_CONFIG
        };

        enum ESkillSlotImg : uint16_t
        {
            SKILL_SLOT_SKILL1 = 0,
            SKILL_SLOT_SKILL2 = 1,
            SKILL_SLOT_SKILL3 = 2,
            SKILL_SLOT_BUFF1 = 3,
            SKILL_SLOT_BUFF2 = 4,
            SKILL_SLOT_BUFF3 = 5
        };

        enum ETextBoxImg : uint16_t
        {
            TEXTBOX_IMG_DISTANCE_TIME = 6,
            TEXTBOX_IMG_SKILL1_TIME = 7,
            TEXTBOX_IMG_SKILL2_TIME = 8,
            TEXTBOX_IMG_ADD_EXTRA_ITEM = 9
        };

    private:
        enum IMAGE_LIST
        {
            IMAGE_BASE_WINDOW_BACK = BITMAP_INTERFACE_NEW_MESSAGEBOX_BEGIN + 3, //. newui_msgbox_back.jpg
            IMAGE_BASE_WINDOW_TOP = BITMAP_INTERFACE_NEW_PERSONALINVENTORY_BEGIN, //. newui_item_back01.tga (190,64)
            IMAGE_BASE_WINDOW_LEFT = BITMAP_INTERFACE_NEW_PERSONALINVENTORY_BEGIN + 2, //. newui_item_back02-l.tga (21,320)
            IMAGE_BASE_WINDOW_RIGHT = BITMAP_INTERFACE_NEW_PERSONALINVENTORY_BEGIN + 3, //. newui_item_back02-r.tga (21,320)
            IMAGE_BASE_WINDOW_BOTTOM = BITMAP_INTERFACE_NEW_PERSONALINVENTORY_BEGIN + 4, //. newui_item_back03.tga (190,45)
            IMAGE_BASE_WINDOW_BTN_EXIT = BITMAP_INTERFACE_NEW_PERSONALINVENTORY_BEGIN + 17, //. newui_exit_00.tga
            //--
            IMAGE_TABLE_SQUARE = BITMAP_INTERFACE_NEW_INVENTORY_BASE_BEGIN, //. newui_item_box.tga
            IMAGE_TABLE_TOP_LEFT, //. newui_item_table01(L).tga (14,14)
            IMAGE_TABLE_TOP_RIGHT, //. newui_item_table01(R).tga (14,14)
            IMAGE_TABLE_BOTTOM_LEFT, //. newui_item_table02(L).tga (14,14)
            IMAGE_TABLE_BOTTOM_RIGHT, //. newui_item_table02(R).tga (14,14)
            IMAGE_TABLE_TOP_PIXEL, //. newui_item_table03(up).tga (1, 14)
            IMAGE_TABLE_BOTTOM_PIXEL, //. newui_item_table03(dw).tga (1,14)
            IMAGE_TABLE_LEFT_PIXEL, //. newui_item_table03(L).tga (14,1)
            IMAGE_TABLE_RIGHT_PIXEL, //. newui_item_table03(R).tga (14,1)
            //--
            IMAGE_WINDOW_TAB_BTN = BITMAP_GUILDINFO_BEGIN,
            //--
            IMAGE_MACROUI_HELPER_RAGEMINUS = BITMAP_INTERFACE_MACROUI_BEGIN, // newui_position02.tga (70, 25)
            IMAGE_MACROUI_HELPER_OPTIONBUTTON = BITMAP_INTERFACE_MACROUI_BEGIN + 1, // newui_position02.tga (70, 25)
            IMAGE_MACROUI_HELPER_INPUTNUMBER = BITMAP_INTERFACE_MACROUI_BEGIN + 2,
            IMAGE_MACROUI_HELPER_INPUTSTRING = BITMAP_INTERFACE_MACROUI_BEGIN + 3,
            //-- Buttons
            IMAGE_CHAINFO_BTN_STAT = BITMAP_INTERFACE_NEW_CHAINFO_WINDOW_BEGIN + 1,
            IMAGE_CLEARNESS_BTN = BITMAP_CURSEDTEMPLE_BEGIN + 4,
            IMAGE_IGS_BUTTON = BITMAP_IGS_MSGBOX_BUTTON,
            IMAGE_CHECKBOX_BTN = BITMAP_OPTION_BEGIN + 5,

            //-- Skills
            IMAGE_SKILL1 = BITMAP_INTERFACE_NEW_SKILLICON_BEGIN,
            IMAGE_SKILL2,
            IMAGE_COMMAND,
            IMAGE_SKILL3,
            IMAGE_SKILLBOX,
            IMAGE_SKILLBOX_USE,
            IMAGE_NON_SKILL1,
            IMAGE_NON_SKILL2,
            IMAGE_NON_COMMAND,
            IMAGE_NON_SKILL3,
        };

        enum CLASS_LIST_
        {
            DW = 0,
            DK,
            FE,
            MG,
            DL,
            SM,
            RF,
        };

        static constexpr int MAX_SKILLS_SLOT = 6;
        static constexpr int WINDOW_WIDTH = 190;
        static constexpr int WINDOW_HEIGHT = 429;

        typedef struct
        {
            int iNumTab;
            BYTE class_character[MAX_CLASS];
            CNewUIButton* btn;
        } CButtonTap;

        typedef struct
        {
            int iNumTab;
            BYTE class_character[MAX_CLASS];
            CNewUICheckBox* box;
        } CheckBoxTap;

        typedef struct
        {
            int iNumTab;
            int s_ImgIndex;
            POINT m_Pos;
            POINT m_Size;
            BYTE class_character[MAX_CLASS];
        } cTexture;

        typedef struct
        {
            int iNumTab;
            POINT m_Pos;
            std::string m_Name;
            BYTE class_character[MAX_CLASS];
        } cTextName;

        typedef std::map<int, CButtonTap> cButtonMap;
        typedef std::map<int, CheckBoxTap> cCheckBoxMap;
        typedef std::map<int, cTexture> cTextureMap;
        typedef std::map<int, cTextName> cTextNameMap;

        public:

        void RenderBtnList();
        int UpdateMouseBtnList();
        void RegisterBtnCharacter(BYTE class_character, int Identifier);
        void RegisterButton(int Identifier, CButtonTap button);
        void InsertButton(int imgindex, int x, int y, int sx, int sy, bool overflg, bool isimgwidth, bool bClickEffect, bool MoveTxt,std::string btname,std::string tooltiptext, int Identifier, int iNumTab);
        //--
        void RenderBoxList();
        int UpdateMouseBoxList();
        void RegisterBoxCharacter(BYTE class_character, int Identifier);
        void RegisterCheckBox(int Identifier, CheckBoxTap button);
        void InsertCheckBox(int imgindex, int x, int y, int sx, int sy, bool overflg,std::string btname, int Identifier, int iNumTab);
        //--
        void RenderIconList();
        int UpdateMouseIconList();
        void RegisterIconCharacter(BYTE class_character, int Identifier);
        void RegisterIcon(int Identifier, cTexture button);
        void InsertIcon(int imgindex, int x, int y, int sx, int sy, int Identifier, int iNumTab);
        //--
        void RenderTextList();
        void RegisterTextCharacter(BYTE class_character, int Identifier);
        void RegisterText(int Identifier, cTextName button);
        void InsertText(int x, int y,std::string Name, int Identifier, int iNumTab);
        void InitText();
        void InitImage();
        void InitButtons();
        void InitCheckBox();
        void InitTextboxInput();
        void SetPos(int x, int y);
        void RenderBack(int x, int y, int width, int height);

        int GetSkillIndex(int iSkill);
        bool IsSkillAssigned(int iSkill);
        void RenderSkillIcon(int iSkill, float x, float y, float width, float height);
       
        void InitConfig();
        void SaveConfig();
        void ApplyConfig();

        void LoadImages();
        void UnloadImages();

        void ApplyConfigFromCheckbox(int iCheckboxId, bool bState);
        void ApplyConfigFromSkillSlot(int iSlot, int iSkill);
        void ApplyHuntRangeUpdate(int iDelta);
        void ApplyLootRangeUpdate(int iDelta);
        void SaveExtraItem();
        void RemoveExtraItem();

    private:
        CNewUIManager* m_pNewUIMng;
        CUITextInputBox m_DistanceTimeInput;
        CUITextInputBox m_Skill2DelayInput;
        CUITextInputBox m_Skill3DelayInput;
        CUITextInputBox m_ItemInput;
        CUIExtraItemListBox m_ItemFilter;

        POINT m_Pos;
        POINT m_SubPos;
        CNewUIRadioGroupButton m_TabBtn;
        int m_iCurrentOpenTab;
        int m_iCurrentOpenSubWin;
        bool m_bSubWinOpen;
        cButtonMap m_ButtonList;
        cCheckBoxMap m_CheckBoxList;
        cTextNameMap m_TextNameList;
        cTextureMap m_IconList;
        int m_iSelectedSkillSlot;
        std::array<int, MAX_SKILLS_SLOT> m_aiSelectedSkills;
    };
}


NewUIMuHelperSkillList.cpp
#include "stdafx.h"
#include <algorithm>
#include <vector>
#include <array>
#include "UIControls.h"
#include "NewUISystem.h"
#include "NewUIMuHelperSkillList.h"
#include "CharacterManager.h"
#include "MUHelper/MuHelper.h"
#include "SkillManager.h"
#include "SPK/ZzzToolKit.h"
#include "SPK/WideData.h"
#include "wsclientinline.h"

SEASON3B::CNewUIMuHelperSkillList::CNewUIMuHelperSkillList()
{
    Reset();
}

SEASON3B::CNewUIMuHelperSkillList::~CNewUIMuHelperSkillList()
{
    Release();
}

bool SEASON3B::CNewUIMuHelperSkillList::Create(CNewUIManager* pNewUIMng, CNewUI3DRenderMng* pNewUI3DRenderMng)
{
    if (NULL == pNewUIMng)
        return false;

    m_pNewUIMng = pNewUIMng;
    m_pNewUIMng->AddUIObj(INTERFACE_MUHELPER_SKILL_LIST, this);

    m_pNewUI3DRenderMng = pNewUI3DRenderMng;

    LoadImages();

    SkillListPos.x = IsToolKit.GetPositionScreen() - 200;
    SkillListPos.y = 0;

    Show(false);

    return true;
}

void SEASON3B::CNewUIMuHelperSkillList::Release()
{
    if (m_pNewUI3DRenderMng)
    {
        m_pNewUI3DRenderMng->DeleteUI2DEffectObject(UI2DEffectCallback);
    }

    UnloadImages();

    if (m_pNewUIMng)
    {
        m_pNewUIMng->RemoveUIObj(this);
        m_pNewUIMng = NULL;
    }
}

void SEASON3B::CNewUIMuHelperSkillList::Reset()
{
    m_bRenderSkillInfo = false;
    m_iRenderSkillInfoType = 0;
    m_iRenderSkillInfoPosX = 0;
    m_iRenderSkillInfoPosY = 0;

    m_EventState = EVENT_NONE;
}

void SEASON3B::CNewUIMuHelperSkillList::LoadImages()
{
    LoadBitmap("Interface\\newui_skill.jpg", IMAGE_SKILL1, GL_LINEAR);
    LoadBitmap("Interface\\newui_skill2.jpg", IMAGE_SKILL2, GL_LINEAR);
    LoadBitmap("Interface\\newui_command.jpg", IMAGE_COMMAND, GL_LINEAR);
    LoadBitmap("Interface\\newui_skillbox.jpg", IMAGE_SKILLBOX, GL_LINEAR);
    LoadBitmap("Interface\\newui_skillbox2.jpg", IMAGE_SKILLBOX_USE, GL_LINEAR);
    LoadBitmap("Interface\\newui_non_skill.jpg", IMAGE_NON_SKILL1, GL_LINEAR);
    LoadBitmap("Interface\\newui_non_skill2.jpg", IMAGE_NON_SKILL2, GL_LINEAR);
    LoadBitmap("Interface\\newui_non_command.jpg", IMAGE_NON_COMMAND, GL_LINEAR);
    LoadBitmap("Interface\\newui_skill3.jpg", IMAGE_SKILL3, GL_LINEAR);
    LoadBitmap("Interface\\newui_non_skill3.jpg", IMAGE_NON_SKILL3, GL_LINEAR);
}

void SEASON3B::CNewUIMuHelperSkillList::UnloadImages()
{
    DeleteBitmap(IMAGE_SKILL1);
    DeleteBitmap(IMAGE_SKILL2);
    DeleteBitmap(IMAGE_COMMAND);
    DeleteBitmap(IMAGE_SKILLBOX);
    DeleteBitmap(IMAGE_SKILLBOX_USE);
    DeleteBitmap(IMAGE_NON_SKILL1);
    DeleteBitmap(IMAGE_NON_SKILL2);
    DeleteBitmap(IMAGE_NON_COMMAND);
    DeleteBitmap(IMAGE_SKILL3);
    DeleteBitmap(IMAGE_NON_SKILL3);
}

bool SEASON3B::CNewUIMuHelperSkillList::UpdateMouseEvent()
{
    if (IsRelease(VK_LBUTTON))
    {
        int skillId = UpdateMouseSkillList();
        if (skillId != -1)
        {
            g_ConsoleDebug->Write(MCD_NORMAL, "[MU Helper] Clicked skill [%d]", skillId);
            g_pNewUIMuHelper->AssignSkill(skillId);
            Show(false);
            return false;
        }

        return true;
    }

    return false;
}

bool SEASON3B::CNewUIMuHelperSkillList::UpdateKeyEvent()
{
    if (IsVisible())
    {
        if (IsPress(VK_ESCAPE) == true)
        {
            g_pNewUISystem->Hide(INTERFACE_MUHELPER_SKILL_LIST);
            SetFocus(g_hWnd);
            //PlayBuffer(SOUND_CLICK01);

            return false;
        }
    }
    return true;
}

void SEASON3B::CNewUIMuHelperSkillList::PrepareSkillsToRender()
{
    m_aiSkillsToRender.clear();
    m_skillIconMap.clear();

    BYTE skillNumber = CharacterAttribute->SkillNumber;
    if (skillNumber > 0)
    {
        for (int i = 0; i < MAX_MAGIC; ++i)
        {
            int iSkillType = CharacterAttribute->Skill[i];

            if (iSkillType != 0 && (iSkillType < AT_SKILL_STUN || iSkillType > AT_SKILL_REMOVAL_BUFF))
            {
                BYTE bySkillUseType = SkillAttribute[iSkillType].SkillUseType;

                if (bySkillUseType == SKILL_USE_TYPE_MASTER || bySkillUseType == SKILL_USE_TYPE_MASTERLEVEL)
                {
                    continue;
                }

                if ((m_bFilterByAttackSkills && IsAttackSkill(iSkillType))
                    || (m_bFilterByBuffSkills && IsBuffSkill(iSkillType)))
                {
                    m_aiSkillsToRender.push_back(iSkillType);
                }
            }
        }
    }
}

bool SEASON3B::CNewUIMuHelperSkillList::Update()
{
    for (const auto& [skillId, icon] : m_skillIconMap)
    {
        if (!icon.isVisible)
            continue;

        RECT rc = { icon.location.x, icon.location.y,
                    icon.location.x + icon.area.cx,
                    icon.location.y + icon.area.cy };

        if (PtInRect(&rc, { MouseX, MouseY }))
        {
            m_bRenderSkillInfo = true;


            m_iRenderSkillInfoType = -1;
            for (int i = 0; i < MAX_MAGIC; ++i)
            {
                if (CharacterAttribute->Skill[i] == skillId)
                {
                    m_iRenderSkillInfoType = i;
                    break;
                }
            }

            m_iRenderSkillInfoPosX = icon.location.x;
            m_iRenderSkillInfoPosY = icon.location.y;
            break;
        }
    }
    return true;
}

bool SEASON3B::CNewUIMuHelperSkillList::Render()
{
    float baseX = SkillListPos.x - 22;
    float baseY = m_bFilterByAttackSkills ? 171 : 293;
    float fOrigY = baseY - 150;

    float width = 32.0f;
    float height = 38.0f;
    const int maxSkillsPerColumn = 10;

    int skillIndex = 0;
    m_skillIconMap.clear();

    for (int iSkillType : m_aiSkillsToRender)
    {
        int column = skillIndex / maxSkillsPerColumn;
        int row = skillIndex % maxSkillsPerColumn;

        float x = baseX - column * (width);
        float y = fOrigY + row * height;

        RenderImage(IMAGE_SKILLBOX, x, y, width, height);
        g_pSkillList->RenderSkillIcon(iSkillType, x + 6, y + 6, 20, 28, 1);

        m_skillIconMap[iSkillType] = {
            iSkillType,
            { static_cast<LONG>(x), static_cast<LONG>(y) },
            { static_cast<LONG>(width), static_cast<LONG>(height) },
            true
        };

        skillIndex++;
    }

    if (m_bRenderSkillInfo && m_pNewUI3DRenderMng)
    {
        m_pNewUI3DRenderMng->RenderUI2DEffect(INVENTORY_CAMERA_Z_ORDER, UI2DEffectCallback, this, 0, 0);
        m_bRenderSkillInfo = false;
    }

    return true;
}


void SEASON3B::CNewUIMuHelperSkillList::RenderSkillInfo()
{
    ::RenderSkillInfo(m_iRenderSkillInfoPosX + 15, m_iRenderSkillInfoPosY + 80, m_iRenderSkillInfoType);
}

float SEASON3B::CNewUIMuHelperSkillList::GetLayerDepth()
{
    return 5.2f;
}

void SEASON3B::CNewUIMuHelperSkillList::RenderSkillIcon(int iSkillType, float x, float y, float width, float height)
{
    float fU, fV;
    int iKindofSkill = 0;

    BYTE bySkillUseType = SkillAttribute[iSkillType].SkillUseType;
    int Skill_Icon = SkillAttribute[iSkillType].Magic_Icon;

    if (iSkillType >= AT_PET_COMMAND_DEFAULT && iSkillType <= AT_PET_COMMAND_END)
    {
        fU = ((iSkillType - AT_PET_COMMAND_DEFAULT) % 8) * width / 256.f;
        fV = ((iSkillType - AT_PET_COMMAND_DEFAULT) / 8) * height / 256.f;
        iKindofSkill = KOS_COMMAND;
    }
    else if (iSkillType == AT_SKILL_PLASMA_STORM_FENRIR)
    {
        fU = 4 * width / 256.f;
        fV = 0.f;
        iKindofSkill = KOS_COMMAND;
    }
    else if ((iSkillType >= AT_SKILL_ALICE_DRAINLIFE && iSkillType <= AT_SKILL_ALICE_THORNS))
    {
        fU = ((iSkillType - AT_SKILL_ALICE_DRAINLIFE) % 8) * width / 256.f;
        fV = 3 * height / 256.f;
        iKindofSkill = KOS_SKILL2;
    }
    else if (iSkillType >= AT_SKILL_ALICE_SLEEP && iSkillType <= AT_SKILL_ALICE_BLIND)
    {
        fU = ((iSkillType - AT_SKILL_ALICE_SLEEP + 4) % 8) * width / 256.f;
        fV = 3 * height / 256.f;
        iKindofSkill = KOS_SKILL2;
    }
    else if (iSkillType == AT_SKILL_ALICE_BERSERKER)
    {
        fU = 10 * width / 256.f;
        fV = 3 * height / 256.f;
        iKindofSkill = KOS_SKILL2;
    }
    else if (iSkillType >= AT_SKILL_ALICE_WEAKNESS && iSkillType <= AT_SKILL_ALICE_ENERVATION)
    {
        fU = (iSkillType - AT_SKILL_ALICE_WEAKNESS + 8) * width / 256.f;
        fV = 3 * height / 256.f;
        iKindofSkill = KOS_SKILL2;
    }
    else if (iSkillType >= AT_SKILL_SUMMON_EXPLOSION && iSkillType <= AT_SKILL_SUMMON_REQUIEM)
    {
        fU = ((iSkillType - AT_SKILL_SUMMON_EXPLOSION + 6) % 8) * width / 256.f;
        fV = 3 * height / 256.f;
        iKindofSkill = KOS_SKILL2;
    }
    else if (iSkillType == AT_SKILL_SUMMON_POLLUTION)
    {
        fU = 11 * width / 256.f;
        fV = 3 * height / 256.f;
        iKindofSkill = KOS_SKILL2;
    }
    else if (iSkillType == AT_SKILL_BLOW_OF_DESTRUCTION)
    {
        fU = 7 * width / 256.f;
        fV = 2 * height / 256.f;
        iKindofSkill = KOS_SKILL2;
    }
    else if (iSkillType == AT_SKILL_GAOTIC)
    {
        fU = 3 * width / 256.f;
        fV = 8 * height / 256.f;
        iKindofSkill = KOS_SKILL2;
    }
    else if (iSkillType == AT_SKILL_RECOVER)
    {
        fU = 9 * width / 256.f;
        fV = 2 * height / 256.f;
        iKindofSkill = KOS_SKILL2;
    }
    else if (iSkillType == AT_SKILL_MULTI_SHOT)
    {
        fU = 0 * width / 256.f;
        fV = 8 * height / 256.f;
        iKindofSkill = KOS_SKILL2;
    }
    else if (iSkillType == AT_SKILL_FLAME_STRIKE)
    {
        int iTypeL = CharacterMachine->Equipment[EQUIPMENT_WEAPON_LEFT].Type;
        int iTypeR = CharacterMachine->Equipment[EQUIPMENT_WEAPON_RIGHT].Type;

        fU = 1 * width / 256.f;
        fV = 8 * height / 256.f;
        iKindofSkill = KOS_SKILL2;
    }
    else if (iSkillType == AT_SKILL_GIGANTIC_STORM)
    {
        fU = 2 * width / 256.f;
        fV = 8 * height / 256.f;
        iKindofSkill = KOS_SKILL2;
    }
    else if (iSkillType == AT_SKILL_LIGHTNING_SHOCK)
    {
        fU = 2 * width / 256.f;
        fV = 3 * height / 256.f;
        iKindofSkill = KOS_SKILL2;
    }
    else if (AT_SKILL_LIGHTNING_SHOCK_UP <= iSkillType && iSkillType <= AT_SKILL_LIGHTNING_SHOCK_UP + 4)
    {
        fU = 6 * width / 256.f;
        fV = 8 * height / 256.f;
        iKindofSkill = KOS_SKILL2;
    }
    else if (iSkillType == AT_SKILL_SWELL_OF_MAGICPOWER)
    {
        fU = 8 * width / 256.f;
        fV = 2 * height / 256.f;
        iKindofSkill = KOS_SKILL2;
    }
    else if (bySkillUseType == 4)
    {
        fU = (width / 256.f) * (Skill_Icon % 12);
        fV = (height / 256.f) * ((Skill_Icon / 12) + 4);
        iKindofSkill = KOS_SKILL2;
    }
    else if (iSkillType >= AT_SKILL_THRUST)
    {
        fU = ((iSkillType - 260) % 12) * width / 256.f;
        fV = ((iSkillType - 260) / 12) * height / 256.f;
        iKindofSkill = KOS_SKILL3;
    }
    else if (iSkillType >= 57)
    {
        fU = ((iSkillType - 57) % 8) * width / 256.f;
        fV = ((iSkillType - 57) / 8) * height / 256.f;
        iKindofSkill = KOS_SKILL2;
    }
    else
    {
        fU = ((iSkillType - 1) % 8) * width / 256.f;
        fV = ((iSkillType - 1) / 8) * height / 256.f;
        iKindofSkill = KOS_SKILL1;
    }
    int iTextureId = 0;
    switch (iKindofSkill)
    {
    case KOS_COMMAND:
    {
        iTextureId = IMAGE_COMMAND;
    }break;
    case KOS_SKILL1:
    {
        iTextureId = IMAGE_SKILL1;
    }break;
    case KOS_SKILL2:
    {
        iTextureId = IMAGE_SKILL2;
    }break;
    case KOS_SKILL3:
    {
        iTextureId = IMAGE_SKILL3;
    }break;
    }

    if (iTextureId != 0)
    {
        RenderBitmap(iTextureId, x, y, width, height, fU, fV, width / 256.f, height / 256.f);
    }
}

bool SEASON3B::CNewUIMuHelperSkillList::IsAttackSkill(int iSkillType)
{
    if (IsBuffSkill(iSkillType))
    {
        return false;
    }

    if (IsDefenseSkill(iSkillType))
    {
        return false;
    }

    if (IsHealingSkill(iSkillType))
    {
        return false;
    }

    return true;
}

bool SEASON3B::CNewUIMuHelperSkillList::IsBuffSkill(int iSkillType)
{
    switch (iSkillType)
    {
            // DK SPK Fix Skill
        case AT_SKILL_VITALITY:
        case MASTER_SKILL_ADD_GREATER_LIFE_IMPROVED:
        case MASTER_SKILL_ADD_GREATER_LIFE_ENHANCED:
            return true;

            // DW => SPK OK ( 2 buff )
        case AT_SKILL_WIZARDDEFENSE:
        case MASTER_SKILL_ADD_MANA_SHIELD_IMPROVED:
        case MASTER_SKILL_ADD_MANA_SHIELD_ENHANCED:

        case AT_SKILL_SWELL_OF_MAGICPOWER:
        case MASTER_SKILL_ADD_MAGIC_CIRCLE_IMPROVED:
        case MASTER_SKILL_ADD_MAGIC_CIRCLE_ENHANCED:
            return true;

            // Elf buffs
        case AT_SKILL_INFINITY_ARROW:
        case MASTER_SKILL_ADD_INFINITY_ARROW_IMPROVED:

        case AT_SKILL_DEFENSE:
        case MASTER_SKILL_ADD_GREATER_DEFENSE_IMPROVED:
        case MASTER_SKILL_ADD_GREATER_DEFENSE_ENHANCED:

        case AT_SKILL_HEALING:
        case MASTER_SKILL_ADD_HEAL_IMPROVED:

        case AT_SKILL_ATTACK:
        case MASTER_SKILL_ADD_GREATER_DAMAGE_IMPROVED:
        case MASTER_SKILL_ADD_GREATER_DAMAGE_ENHANCED:
            return true;

        // DL buffs
        case AT_SKILL_ADD_CRITICAL:
        case MASTER_SKILL_ADD_GREATER_CRITICAL_DAMAGE_IMPROVED:
        case MASTER_SKILL_ADD_GREATER_CRITICAL_DAMAGE_ENHANCED:
        return true;

            // SM buffs
        case AT_SKILL_ALICE_BERSERKER:
        case MASTER_SKILL_ADD_SWORD_POWER_IMPROVED:
        case MASTER_SKILL_ADD_SWORD_POWER_ENHANCED:
        case MASTER_SKILL_ADD_SWORD_POWER_MASTERED:

        case AT_SKILL_ALICE_THORNS:
            return true;

            // RF Buff
        case AT_SKILL_ATT_UP_OURFORCES:

        case AT_SKILL_HP_UP_OURFORCES:
        case AT_SKILL_DEF_UP_OURFORCES:
        case MASTER_SKILL_ADD_GREATER_DEFENSE_SUCCESS_RATE_IMPROVED:
        case MASTER_SKILL_ADD_GREATER_DEFENSE_SUCCESS_RATE_ENHANCED:
        case MASTER_SKILL_ADD_FITNESS_IMPROVED:
        return true;
    }

    return false;
}

bool SEASON3B::CNewUIMuHelperSkillList::IsHealingSkill(int iSkillType)
{
    // To-do: Complete list of healing skills

    switch (iSkillType)
    {
    case AT_SKILL_HEAL_UP:
    case AT_SKILL_HEAL_UP + 1:
    case AT_SKILL_HEAL_UP + 2:
    case AT_SKILL_HEAL_UP + 3:
    case AT_SKILL_HEAL_UP + 4:
    case AT_SKILL_HEALING:
        return true;
    case AT_SKILL_ALICE_DRAINLIFE_UP:
    case AT_SKILL_ALICE_DRAINLIFE_UP + 1:
    case AT_SKILL_ALICE_DRAINLIFE_UP + 2:
    case AT_SKILL_ALICE_DRAINLIFE_UP + 3:
    case AT_SKILL_ALICE_DRAINLIFE_UP + 4:
    case AT_SKILL_ALICE_DRAINLIFE:
        return true;
    }

    return false;
}

bool SEASON3B::CNewUIMuHelperSkillList::IsDefenseSkill(int iSkillType)
{
    switch (iSkillType)
    {
    case AT_SKILL_DEFENSE:
        return true;
    }

    return false;
}

void SEASON3B::CNewUIMuHelperSkillList::FilterByAttackSkills()
{
    m_bFilterByAttackSkills = true;
    m_bFilterByBuffSkills = false;

    PrepareSkillsToRender();
}

void SEASON3B::CNewUIMuHelperSkillList::FilterByBuffSkills()
{
    m_bFilterByBuffSkills = true;
    m_bFilterByAttackSkills = false;

    PrepareSkillsToRender();
}

void SEASON3B::CNewUIMuHelperSkillList::UI2DEffectCallback(LPVOID pClass, DWORD dwParamA, DWORD dwParamB)
{
    if (auto* pThis = reinterpret_cast<CNewUIMuHelperSkillList*>(pClass))
    {
        pThis->RenderSkillInfo();
    }
}

int SEASON3B::CNewUIMuHelperSkillList::UpdateMouseSkillList()
{
    auto li = m_skillIconMap.begin();

    for (; li != m_skillIconMap.end(); li++)
    {
        cSkillIcon* pIcon = &li->second;

        if (CheckMouseIn(pIcon->location.x, pIcon->location.y, pIcon->area.cx, pIcon->area.cy))
        {
            return li->first;
        }
    }

    return -1;
}

Bon Dia

Dakosmu

NewUIMuHelperConfig.cpp

#include "stdafx.h"

#include <algorithm>
#include <vector>
#include <array>

#include "UIControls.h"
#include "NewUISystem.h"
#include "NewUIMuHelperConfig.h"
#include "CharacterManager.h"
#include "MUHelper/MuHelper.h"
#include "SkillManager.h"
#include "SPK/ZzzToolKit.h"
#include "SPK/WideData.h"
#include "wsclientinline.h"

using namespace MUHelper;

constexpr int MAX_NUMBER_DIGITS = 3;

ConfigData _TempConfig;

SEASON3B::CNewUIMuHelperConfig::CNewUIMuHelperConfig()
{
    m_pNewUIMng = NULL;
    m_Pos.x = 0;
    m_Pos.y = 0;
    m_iCurrentPage = -1;
    m_iCurrentHealThreshold = 50;
    m_iCurrentPartyHealThreshold = 50;
    m_iCurrentPotionThreshold = 50;
}

SEASON3B::CNewUIMuHelperConfig::~CNewUIMuHelperConfig()
{
    Release();
}

bool SEASON3B::CNewUIMuHelperConfig::Create(CNewUIManager* pNewUIMng, int x, int y)
{
    if (NULL == pNewUIMng)
        return false;

    m_pNewUIMng = pNewUIMng;
    m_pNewUIMng->AddUIObj(INTERFACE_MUHELPER_EXT, this);

    SetPos(x, y);

    LoadImages();

    InitButtons();

    InitText();

    Show(false);

    return true;
}

void SEASON3B::CNewUIMuHelperConfig::Release()
{
    UnloadImages();

    if (m_pNewUIMng)
    {
        m_pNewUIMng->RemoveUIObj(this);
        m_pNewUIMng = NULL;
    }
}

void SEASON3B::CNewUIMuHelperConfig::SetPos(int x, int y)
{
    m_Pos.x = x;
    m_Pos.y = y;
}

void SEASON3B::CNewUIMuHelperConfig::InitText()
{
    m_BuffTimeInput.Init(g_hWnd, 17, 15, MAX_NUMBER_DIGITS, false);
    m_BuffTimeInput.SetTextColor(255, 0, 0, 0);
    m_BuffTimeInput.SetBackColor(255, 255, 255, 255);
    m_BuffTimeInput.SetFont(g_hFont);
    m_BuffTimeInput.SetState(UISTATE_NORMAL);
    m_BuffTimeInput.SetOption(UIOPTION_NUMBERONLY);
}

void SEASON3B::CNewUIMuHelperConfig::InitButtons()
{
    m_BtnPreConHuntRange.CheckBoxImgState(IMAGE_MACROUI_HELPER_OPTIONBUTTON);
    m_BtnPreConHuntRange.CheckBoxInfo(m_Pos.x + 17, m_Pos.y + 78, 15, 15);
    m_BtnPreConHuntRange.ChangeText(GlobalText[3555]);

    m_BtnPreConAttacking.CheckBoxImgState(IMAGE_MACROUI_HELPER_OPTIONBUTTON);
    m_BtnPreConAttacking.CheckBoxInfo(m_Pos.x + 17, m_Pos.y + 93, 15, 15);
    m_BtnPreConAttacking.ChangeText(GlobalText[3556]);

    m_BtnSubConMoreThanTwo.CheckBoxImgState(IMAGE_MACROUI_HELPER_OPTIONBUTTON);
    m_BtnSubConMoreThanTwo.CheckBoxInfo(m_Pos.x + 17, m_Pos.y + 143, 15, 15);
    m_BtnSubConMoreThanTwo.ChangeText(GlobalText[3557]);

    m_BtnSubConMoreThanThree.CheckBoxImgState(IMAGE_MACROUI_HELPER_OPTIONBUTTON);
    m_BtnSubConMoreThanThree.CheckBoxInfo(m_Pos.x + 17, m_Pos.y + 158, 15, 15);
    m_BtnSubConMoreThanThree.ChangeText(GlobalText[3558]);

    m_BtnSubConMoreThanFour.CheckBoxImgState(IMAGE_MACROUI_HELPER_OPTIONBUTTON);
    m_BtnSubConMoreThanFour.CheckBoxInfo(m_Pos.x + 17 + 78, m_Pos.y + 143, 15, 15);
    m_BtnSubConMoreThanFour.ChangeText(GlobalText[3559]);

    m_BtnSubConMoreThanFive.CheckBoxImgState(IMAGE_MACROUI_HELPER_OPTIONBUTTON);
    m_BtnSubConMoreThanFive.CheckBoxInfo(m_Pos.x + 17 + 78, m_Pos.y + 158, 15, 15);
    m_BtnSubConMoreThanFive.ChangeText(GlobalText[3560]);

    m_BtnPartyHeal.CheckBoxImgState(IMAGE_OPTION_BTN_CHECK);
    m_BtnPartyHeal.CheckBoxInfo(m_Pos.x + 17, m_Pos.y + 78, 15, 15);
    m_BtnPartyHeal.ChangeText(GlobalText[3539]);

    m_BtnPartyDuration.CheckBoxImgState(IMAGE_OPTION_BTN_CHECK);
    m_BtnPartyDuration.CheckBoxInfo(m_Pos.x + 17, m_Pos.y + 168, 15, 15);
    m_BtnPartyDuration.ChangeText(GlobalText[3540]);

    m_BtnSave.ChangeButtonImgState(1, IMAGE_IGS_BUTTON, 1, 0, 1);
    m_BtnSave.ChangeButtonInfo(m_Pos.x + 120, m_Pos.y + 388, 52, 26);
    m_BtnSave.ChangeText(GlobalText[3503]);
    m_BtnSave.MoveTextPos(0, -1);
    m_BtnSave.ChangeToolTipText("", TRUE);

    m_BtnReset.ChangeButtonImgState(1, IMAGE_IGS_BUTTON, 1, 0, 1);
    m_BtnReset.ChangeButtonInfo(m_Pos.x + 65, m_Pos.y + 388, 52, 26);
    m_BtnReset.ChangeText(GlobalText[3504]);
    m_BtnReset.MoveTextPos(0, -1);
    m_BtnReset.ChangeToolTipText("", TRUE);

    m_BtnClose.ChangeButtonImgState(1, IMAGE_BASE_WINDOW_BTN_EXIT, 0, 0, 0);
    m_BtnClose.ChangeButtonInfo(m_Pos.x + 20, m_Pos.y + 388, 36, 29);
    m_BtnClose.ChangeText("");
    m_BtnClose.ChangeToolTipText(GlobalText[388], TRUE);
}

bool SEASON3B::CNewUIMuHelperConfig::Render()
{
    EnableAlphaTest();
    glColor4f(1.f, 1.f, 1.f, 1.f);

    DWORD TextColor = g_pRenderText->GetTextColor();

    g_pRenderText->SetFont(g_hFont);
    g_pRenderText->SetTextColor(HEX_COLOR_WHITE);
    g_pRenderText->SetBgColor(0);

    RenderImage(IMAGE_BASE_WINDOW_BACK, m_Pos.x + 2, m_Pos.y + 3, float(WINDOW_WIDTH - 4), float(WINDOW_HEIGHT - 3));
    RenderImage(IMAGE_BASE_WINDOW_TOP, m_Pos.x, m_Pos.y, float(WINDOW_WIDTH), 64.f);
    RenderImage(IMAGE_BASE_WINDOW_LEFT, m_Pos.x, m_Pos.y + 64.f, 21.f, float(WINDOW_HEIGHT) - 64.f - 45.f);
    RenderImage(IMAGE_BASE_WINDOW_RIGHT, m_Pos.x + float(WINDOW_WIDTH) - 21.f, m_Pos.y + 64.f, 21.f, float(WINDOW_HEIGHT) - 64.f - 45.f);
    RenderImage(IMAGE_BASE_WINDOW_BOTTOM, m_Pos.x, m_Pos.y + float(WINDOW_HEIGHT) - 45.f, float(WINDOW_WIDTH), 45.f);

    g_pRenderText->SetFont(g_hFont);

    if (m_iCurrentPage == SUB_PAGE_POTION_CONFIG_ELF)
    {
        g_pRenderText->RenderText(m_Pos.x, m_Pos.y + 13, GlobalText[3553], 190, 0, RT3_SORT_CENTER); // "Auto Recovery"
        RenderBackPane(m_Pos.x + 12, m_Pos.y + 55, 165, 45, GlobalText[3545]); // "Auto Potion"
        RenderHpLevel(m_Pos.x + 32, m_Pos.y + 80, 124.f, 16.f, m_iCurrentPotionThreshold, GlobalText[3547]); // "HP Status"

        RenderBackPane(m_Pos.x + 12, m_Pos.y + 120, 165, 45, GlobalText[3546]); // "Auto Hea"
        RenderHpLevel(m_Pos.x + 32, m_Pos.y + 145, 124.f, 16.f, m_iCurrentHealThreshold, GlobalText[3547]); // "HP Status"
    }
    else if (m_iCurrentPage == SUB_PAGE_POTION_CONFIG_SUMMY)
    {
        g_pRenderText->RenderText(m_Pos.x, m_Pos.y + 13, GlobalText[3553], 190, 0, RT3_SORT_CENTER); // "Auto Recovery"
        RenderBackPane(m_Pos.x + 12, m_Pos.y + 55, 165, 45, GlobalText[3545]); // "Auto Potion"
        RenderHpLevel(m_Pos.x + 32, m_Pos.y + 80, 124.f, 16.f, m_iCurrentPotionThreshold, GlobalText[3547]); // "HP Status"

        RenderBackPane(m_Pos.x + 12, m_Pos.y + 120, 165, 45, GlobalText[3517]); // "Drain Life"
        RenderHpLevel(m_Pos.x + 32, m_Pos.y + 145, 124.f, 16.f, m_iCurrentHealThreshold, GlobalText[3547]); // "HP Status"
    }
    else if (m_iCurrentPage == SUB_PAGE_POTION_CONFIG)
    {
        g_pRenderText->RenderText(m_Pos.x, m_Pos.y + 13, GlobalText[3553], 190, 0, RT3_SORT_CENTER); // "Auto Recovery"
        RenderBackPane(m_Pos.x + 12, m_Pos.y + 55, 165, 45, GlobalText[3545]); // "Auto Potion"

        RenderHpLevel(m_Pos.x + 32, m_Pos.y + 80, 124.f, 16.f, m_iCurrentPotionThreshold, GlobalText[3547]);
    }
    else if (m_iCurrentPage == SUB_PAGE_SKILL2_CONFIG
        || m_iCurrentPage == SUB_PAGE_SKILL3_CONFIG)
    {
        g_pRenderText->RenderText(m_Pos.x, m_Pos.y + 13, GlobalText[3552], 190, 0, RT3_SORT_CENTER); // "Activation Skil"
        RenderBackPane(m_Pos.x + 12, m_Pos.y + 55, 165, 45, GlobalText[3543]);  // "Pre-con"
        m_BtnPreConHuntRange.Render();
        m_BtnPreConAttacking.Render();

        RenderBackPane(m_Pos.x + 12, m_Pos.y + 120, 165, 45, GlobalText[3544]); // "Sub-con"
        m_BtnSubConMoreThanTwo.Render();
        m_BtnSubConMoreThanThree.Render();
        m_BtnSubConMoreThanFour.Render();
        m_BtnSubConMoreThanFive.Render();
    }

    else if (m_iCurrentPage == SUB_PAGE_PARTY_CONFIG)
    {
        g_pRenderText->RenderText(m_Pos.x, m_Pos.y + 13, GlobalText[3554], 190, 0, RT3_SORT_CENTER); // "Party"
        //g_pRenderText->SetTextColor(TextColor);
        RenderBackPane(m_Pos.x + 12, m_Pos.y + 55, 165, 45, GlobalText[3549]); // Buff Support
        m_BtnPartyDuration.Render();
        g_pRenderText->RenderText(m_Pos.x + 40, m_Pos.y + 97, GlobalText[3551], 124, 0, RT3_SORT_LEFT); // "Time Space of Casting Buff"
        RenderImage(IMAGE_MACROUI_HELPER_INPUTNUMBER, m_Pos.x + 125, m_Pos.y + 93, 20, 15);
        m_BuffTimeInput.Render();
        g_pRenderText->RenderText(m_Pos.x + 146, m_Pos.y + 97, "s", 124, 0, RT3_SORT_LEFT); // "s"
    }
    else if (m_iCurrentPage == SUB_PAGE_PARTY_CONFIG_ELF)
    {
        g_pRenderText->RenderText(m_Pos.x, m_Pos.y + 13, GlobalText[3554], 190, 0, RT3_SORT_CENTER); // "Party"
        //g_pRenderText->SetTextColor(TextColor);
        RenderBackPane(m_Pos.x + 12, m_Pos.y + 55, 165, 70, GlobalText[3548]); // Heal Support
        m_BtnPartyHeal.Render();
        RenderHpLevel(m_Pos.x + 32, m_Pos.y + 100, 124.f, 16.f, m_iCurrentPartyHealThreshold, GlobalText[3550]); // "HP Status of Party Members"

        RenderBackPane(m_Pos.x + 12, m_Pos.y + 145, 165, 45, GlobalText[3549]); // Buff Support
        m_BtnPartyDuration.Render();
        g_pRenderText->RenderText(m_Pos.x + 40, m_Pos.y + 187, GlobalText[3551], 124, 0, RT3_SORT_LEFT); // "Time Space of Casting Buff"
        RenderImage(IMAGE_MACROUI_HELPER_INPUTNUMBER, m_Pos.x + 125, m_Pos.y + 183, 20, 15);
        m_BuffTimeInput.Render();
        g_pRenderText->RenderText(m_Pos.x + 146, m_Pos.y + 187, "s", 124, 0, RT3_SORT_LEFT); // "s"
    }

    m_BtnSave.Render();
    m_BtnReset.Render();
    m_BtnClose.Render();

    DisableAlphaBlend();

    return true;
}

void SEASON3B::CNewUIMuHelperConfig::RenderHpLevel(int x, int y, int width, int height, int level, const char* pszLabel)
{
    RenderImage(IMAGE_OPTION_VOLUME_BACK, x, y, 124.f, 16.f);
    if (level > 0)
    {
        RenderImage(IMAGE_OPTION_VOLUME_COLOR, x, y, 124.f * 0.1f * (level), 16.f);
    }
    g_pRenderText->RenderText(x, y + 18, pszLabel, width, 0, RT3_SORT_CENTER);
}

void SEASON3B::CNewUIMuHelperConfig::RenderBackPane(int x, int y, int width, int height, const char* pszHeader)
{
    DWORD TextColor = g_pRenderText->GetTextColor();
    int headerWidth = 65;

    EnableAlphaTest();
    glColor4f(0.0, 0.0, 0.0, 0.4f);
    RenderColor(x + 3.f, y + 2.f, headerWidth - 7.f, 18.f, 0.0, 0);  // shade for top box
    RenderColor(x + 3.f, y + 2.f + 18.f, width - 7.f, height - 7.f, 0.0, 0);  // shade for bottom box
    EndRenderColor();

    // Top box (tab) without bottom line
    RenderImage(IMAGE_TABLE_TOP_LEFT, x, y, 14.0, 14.0);                                // Top-left corner of the tab
    RenderImage(IMAGE_TABLE_TOP_RIGHT, (x + headerWidth) - 14.f, y, 14.0, 14.0);        // Top-right corner of the tab
    RenderImage(IMAGE_TABLE_TOP_PIXEL, x + 6.f, y, (headerWidth - 12.f), 14.0);         // Top edge of the tab
    RenderImage(IMAGE_TABLE_RIGHT_PIXEL, (x + headerWidth) - 14.f, y + 6.f, 14.0, 14.0); // Right edge of the tab

    // Bottom box without top line
    RenderImage(IMAGE_TABLE_TOP_RIGHT, (x + width) - 14.f, y + 18.f, 14.0, 14.0);       // Main box top-right corner
    RenderImage(IMAGE_TABLE_BOTTOM_LEFT, x, (y + height + 18.f) - 14.f, 14.0, 14.0);    // Main box bottom-left corner
    RenderImage(IMAGE_TABLE_BOTTOM_RIGHT, (x + width) - 14.f, (y + height + 18.f) - 14.f, 14.0, 14.0); // Main box bottom-right corner
    RenderImage(IMAGE_TABLE_TOP_PIXEL, x + 2.f, y + 18.f, (width - 12.f), 14.0);        // Top edge of main box
    RenderImage(IMAGE_TABLE_RIGHT_PIXEL, (x + width) - 14.f, y + 24.f, 14.0, (height - 14.f)); // Right edge of main box
    RenderImage(IMAGE_TABLE_BOTTOM_PIXEL, x + 6.f, (y + height + 18.f) - 14.f, (width - 12.f), 14.0); // Bottom edge of main box

    // Left line to connect top box and bottom box
    RenderImage(IMAGE_TABLE_LEFT_PIXEL, x, y + 6.f, 14.0, (height));             // Connecting left edge

    // Header inside top box
    g_pRenderText->SetTextColor(TextColor);
    g_pRenderText->SetFont(g_hFont);
    g_pRenderText->RenderText(x + 10.f, y + 6.f, pszHeader, headerWidth, 0, RT3_SORT_LEFT);
}

void SEASON3B::CNewUIMuHelperConfig::LoadImages()
{
    LoadBitmap("Interface\\MacroUI\\MacroUI_RangeMinus.tga", IMAGE_MACROUI_HELPER_RAGEMINUS, GL_LINEAR, GL_CLAMP, 1, 0);
    LoadBitmap("Interface\\MacroUI\\MacroUI_OptionButton.tga", IMAGE_MACROUI_HELPER_OPTIONBUTTON, GL_LINEAR, GL_CLAMP, 1, 0);
    LoadBitmap("Interface\\MacroUI\\MacroUI_InputNumber.tga", IMAGE_MACROUI_HELPER_INPUTNUMBER, GL_LINEAR, GL_CLAMP, 1, 0);
    LoadBitmap("Interface\\MacroUI\\MacroUI_InputString.tga", IMAGE_MACROUI_HELPER_INPUTSTRING, GL_LINEAR, GL_CLAMP, 1, 0);
    //--
    LoadBitmap("Interface\\InGameShop\\Ingame_Bt03.tga", IMAGE_IGS_BUTTON, GL_LINEAR, GL_CLAMP, 1, 0);
}

void SEASON3B::CNewUIMuHelperConfig::UnloadImages()
{
    DeleteBitmap(IMAGE_MACROUI_HELPER_RAGEMINUS);
    DeleteBitmap(IMAGE_MACROUI_HELPER_OPTIONBUTTON);
    DeleteBitmap(IMAGE_MACROUI_HELPER_INPUTNUMBER);
    DeleteBitmap(IMAGE_MACROUI_HELPER_INPUTSTRING);
    //--
    DeleteBitmap(IMAGE_IGS_BUTTON);
}

bool SEASON3B::CNewUIMuHelperConfig::Update()
{
    if (IsVisible())
    {
        if (m_iCurrentPage == SUB_PAGE_SKILL2_CONFIG || m_iCurrentPage == SUB_PAGE_SKILL3_CONFIG)
        {
            int iSkillIndex = m_iCurrentPage == SUB_PAGE_SKILL2_CONFIG ? 1 : 2; // SKill 2 : Skill 3

            if (m_BtnPreConHuntRange.UpdateMouseEvent())
            {
                m_BtnPreConHuntRange.RegisterBoxState(true);
                m_BtnPreConAttacking.RegisterBoxState(!m_BtnPreConHuntRange.GetBoxState());

                // Clear other precondition bits and set the bit for "Hunt Range"
                _TempConfig.aiSkillCondition[iSkillIndex] =
                    (_TempConfig.aiSkillCondition[iSkillIndex] & MUHELPER_SKILL_PRECON_CLEAR) |
                    ON_MOBS_NEARBY;
            }
            else if (m_BtnPreConAttacking.UpdateMouseEvent())
            {
                m_BtnPreConAttacking.RegisterBoxState(true);
                m_BtnPreConHuntRange.RegisterBoxState(!m_BtnPreConAttacking.GetBoxState());

                // Clear other precondition bits and set the bit for "Attacking"
                _TempConfig.aiSkillCondition[iSkillIndex] =
                    (_TempConfig.aiSkillCondition[iSkillIndex] & MUHELPER_SKILL_PRECON_CLEAR) |
                    ON_MOBS_ATTACKING;
            }
            else if (m_BtnSubConMoreThanTwo.UpdateMouseEvent())
            {
                m_BtnSubConMoreThanTwo.RegisterBoxState(true);
                m_BtnSubConMoreThanThree.RegisterBoxState(false);
                m_BtnSubConMoreThanFour.RegisterBoxState(false);
                m_BtnSubConMoreThanFive.RegisterBoxState(false);

                // Clear other bits and set the bit for "More Than Two Mobs"
                _TempConfig.aiSkillCondition[iSkillIndex] =
                    (_TempConfig.aiSkillCondition[iSkillIndex] & MUHELPER_SKILL_SUBCON_CLEAR) |
                    ON_MORE_THAN_TWO_MOBS;
            }
            else if (m_BtnSubConMoreThanThree.UpdateMouseEvent())
            {
                m_BtnSubConMoreThanTwo.RegisterBoxState(false);
                m_BtnSubConMoreThanThree.RegisterBoxState(true);
                m_BtnSubConMoreThanFour.RegisterBoxState(false);
                m_BtnSubConMoreThanFive.RegisterBoxState(false);

                // Clear other bits and set the bit for "More Than Three Mobs"
                _TempConfig.aiSkillCondition[iSkillIndex] =
                    (_TempConfig.aiSkillCondition[iSkillIndex] & MUHELPER_SKILL_SUBCON_CLEAR) |
                    ON_MORE_THAN_THREE_MOBS;
            }
            else if (m_BtnSubConMoreThanFour.UpdateMouseEvent())
            {
                m_BtnSubConMoreThanTwo.RegisterBoxState(false);
                m_BtnSubConMoreThanThree.RegisterBoxState(false);
                m_BtnSubConMoreThanFour.RegisterBoxState(true);
                m_BtnSubConMoreThanFive.RegisterBoxState(false);

                // Clear other bits and set the bit for "More Than Four Mobs"
                _TempConfig.aiSkillCondition[iSkillIndex] =
                    (_TempConfig.aiSkillCondition[iSkillIndex] & MUHELPER_SKILL_SUBCON_CLEAR) |
                    ON_MORE_THAN_FOUR_MOBS;
            }
            else if (m_BtnSubConMoreThanFive.UpdateMouseEvent())
            {
                m_BtnSubConMoreThanTwo.RegisterBoxState(false);
                m_BtnSubConMoreThanThree.RegisterBoxState(false);
                m_BtnSubConMoreThanFour.RegisterBoxState(false);
                m_BtnSubConMoreThanFive.RegisterBoxState(true);

                // Clear other bits and set the bit for "More Than Five Mobs"
                _TempConfig.aiSkillCondition[iSkillIndex] =
                    (_TempConfig.aiSkillCondition[iSkillIndex] & MUHELPER_SKILL_SUBCON_CLEAR) |
                    ON_MORE_THAN_FIVE_MOBS;
            }
        }

        if (m_iCurrentPage == SUB_PAGE_PARTY_CONFIG_ELF)
        {
            if (m_BtnPartyHeal.UpdateMouseEvent())
            {
                _TempConfig.bAutoHealParty = m_BtnPartyHeal.GetBoxState();
            }
        }

        if (m_iCurrentPage == SUB_PAGE_PARTY_CONFIG || m_iCurrentPage == SUB_PAGE_PARTY_CONFIG_ELF)
        {
            if (m_BtnPartyDuration.UpdateMouseEvent())
            {
                _TempConfig.bBuffDurationParty = m_BtnPartyDuration.GetBoxState();
            }
        }

        if (m_BtnClose.UpdateMouseEvent())
        {
            g_pNewUISystem->Hide(INTERFACE_MUHELPER_EXT);
        }
        else if (m_BtnSave.UpdateMouseEvent())
        {
            Save();
            g_pNewUISystem->Hide(INTERFACE_MUHELPER_EXT);
        }
        else if (m_BtnReset.UpdateMouseEvent())
        {
            Reset();
        }
    }
    return true;
}

bool SEASON3B::CNewUIMuHelperConfig::UpdateMouseEvent()
{
    // Ignore events outside MU Helper window
    if (!CheckMouseIn(m_Pos.x, m_Pos.y, WINDOW_WIDTH, WINDOW_HEIGHT))
    {
        return true;
    }

    if (CheckMouseIn(m_Pos.x + 33 - 8, m_Pos.y + 80, 124 + 8, 16))
    {
        int iOldValue = m_iCurrentPotionThreshold;
        if (MouseWheel > 0)
        {
            MouseWheel = 0;
            m_iCurrentPotionThreshold++;
            if (m_iCurrentPotionThreshold > 10)
            {
                m_iCurrentPotionThreshold = 10;
            }
        }
        else if (MouseWheel < 0)
        {
            MouseWheel = 0;
            m_iCurrentPotionThreshold--;
            if (m_iCurrentPotionThreshold < 0)
            {
                m_iCurrentPotionThreshold = 0;
            }
        }
        if (IsRepeat(VK_LBUTTON))
        {
            int x = MouseX - (m_Pos.x + 33);
            if (x < 0)
            {
                m_iCurrentPotionThreshold = 0;
            }
            else
            {
                float fValue = (10.f * x) / 124.f;
                m_iCurrentPotionThreshold = (int)fValue + 1;
            }
        }
    }

    if (m_iCurrentPage == SUB_PAGE_POTION_CONFIG_ELF || m_iCurrentPage == SUB_PAGE_POTION_CONFIG_SUMMY)
    {
        if (CheckMouseIn(m_Pos.x + 33 - 8, m_Pos.y + 145, 124 + 8, 16))
        {
            int iOldValue = m_iCurrentHealThreshold;
            if (MouseWheel > 0)
            {
                MouseWheel = 0;
                m_iCurrentHealThreshold++;
                if (m_iCurrentHealThreshold > 10)
                {
                    m_iCurrentHealThreshold = 10;
                }
            }
            else if (MouseWheel < 0)
            {
                MouseWheel = 0;
                m_iCurrentHealThreshold--;
                if (m_iCurrentHealThreshold < 0)
                {
                    m_iCurrentHealThreshold = 0;
                }
            }
            if (IsRepeat(VK_LBUTTON))
            {
                int x = MouseX - (m_Pos.x + 33);
                if (x < 0)
                {
                    m_iCurrentHealThreshold = 0;
                }
                else
                {
                    float fValue = (10.f * x) / 124.f;
                    m_iCurrentHealThreshold = (int)fValue + 1;
                }
            }
        }
    }
    else if (m_iCurrentPage == SUB_PAGE_PARTY_CONFIG_ELF)
    {
        if (CheckMouseIn(m_Pos.x + 32 - 8, m_Pos.y + 100, 124 + 8, 16))
        {
            int iOldValue = m_iCurrentPartyHealThreshold;
            if (MouseWheel > 0)
            {
                MouseWheel = 0;
                m_iCurrentPartyHealThreshold++;
                if (m_iCurrentPartyHealThreshold > 10)
                {
                    m_iCurrentPartyHealThreshold = 10;
                }
            }
            else if (MouseWheel < 0)
            {
                MouseWheel = 0;
                m_iCurrentPartyHealThreshold--;
                if (m_iCurrentPartyHealThreshold < 0)
                {
                    m_iCurrentPartyHealThreshold = 0;
                }
            }
            if (IsRepeat(VK_LBUTTON))
            {
                int x = MouseX - (m_Pos.x + 33);
                if (x < 0)
                {
                    m_iCurrentPartyHealThreshold = 0;
                }
                else
                {
                    float fValue = (10.f * x) / 124.f;
                    m_iCurrentPartyHealThreshold = (int)fValue + 1;
                }
            }
        }
        else if (CheckMouseIn(m_BuffTimeInput.GetPosition_x(), m_BuffTimeInput.GetPosition_y(), 20, 15))
        {
            m_BuffTimeInput.GiveFocus();
        }
        else
        {
            SetFocus(g_hWnd);
        }
    }
    else if (m_iCurrentPage == SUB_PAGE_PARTY_CONFIG)
    {
        if (CheckMouseIn(m_BuffTimeInput.GetPosition_x(), m_BuffTimeInput.GetPosition_y(), 20, 15))
        {
            m_BuffTimeInput.GiveFocus();
        }
        else
        {
            SetFocus(g_hWnd);
        }
    }

    return false;
}

bool SEASON3B::CNewUIMuHelperConfig::UpdateKeyEvent()
{
    if (IsVisible())
    {
        if (IsPress(VK_ESCAPE) == true)
        {
            g_pNewUISystem->Hide(INTERFACE_MUHELPER_EXT);
            //PlayBuffer(SOUND_CLICK01);

            return false;
        }
    }
    return true;
}

float SEASON3B::CNewUIMuHelperConfig::GetLayerDepth()
{
    return 3.4;
}

float SEASON3B::CNewUIMuHelperConfig::GetKeyEventOrder()
{
    return 3.4;
}

void SEASON3B::CNewUIMuHelperConfig::Toggle(int iPageId)
{
    int iPrevPage = m_iCurrentPage;
    m_iCurrentPage = iPageId;

    if (IsVisible() && m_iCurrentPage == iPrevPage)
    {
        m_iCurrentPage = -1;
        this->Show(false);
        return;
    }

    if (m_iCurrentPage == SUB_PAGE_SKILL2_CONFIG || m_iCurrentPage == SUB_PAGE_SKILL3_CONFIG)
    {
        int iSkillIndex = m_iCurrentPage == SUB_PAGE_SKILL2_CONFIG ? 1 : 2;
        m_BtnPreConHuntRange.RegisterBoxState(_TempConfig.aiSkillCondition[iSkillIndex] & ON_MOBS_NEARBY);
        m_BtnPreConAttacking.RegisterBoxState(_TempConfig.aiSkillCondition[iSkillIndex] & ON_MOBS_ATTACKING);
        m_BtnSubConMoreThanTwo.RegisterBoxState(_TempConfig.aiSkillCondition[iSkillIndex] & ON_MORE_THAN_TWO_MOBS);
        m_BtnSubConMoreThanThree.RegisterBoxState(_TempConfig.aiSkillCondition[iSkillIndex] & ON_MORE_THAN_THREE_MOBS);
        m_BtnSubConMoreThanFour.RegisterBoxState(_TempConfig.aiSkillCondition[iSkillIndex] & ON_MORE_THAN_FOUR_MOBS);
        m_BtnSubConMoreThanFive.RegisterBoxState(_TempConfig.aiSkillCondition[iSkillIndex] & ON_MORE_THAN_FIVE_MOBS);
    }
    else if (m_iCurrentPage == SUB_PAGE_POTION_CONFIG || m_iCurrentPage == SUB_PAGE_POTION_CONFIG_ELF || m_iCurrentPage == SUB_PAGE_POTION_CONFIG_SUMMY)
    {
        m_iCurrentPotionThreshold = _TempConfig.iPotionThreshold / 10;
        m_iCurrentHealThreshold = _TempConfig.iHealThreshold / 10;
    }
    else if (m_iCurrentPage == SUB_PAGE_PARTY_CONFIG)
    {
        m_BtnPartyDuration.CheckBoxInfo(m_Pos.x + 17, m_Pos.y + 78, 15, 15);
        m_BtnPartyDuration.RegisterBoxState(_TempConfig.bBuffDurationParty);
        m_iCurrentPartyHealThreshold = _TempConfig.iHealPartyThreshold / 10;

        char wsBuffTime[MAX_NUMBER_DIGITS + 1] = { 0 };
        snprintf(wsBuffTime, MAX_NUMBER_DIGITS + 1, "%d", _TempConfig.iBuffCastInterval);
        m_BuffTimeInput.SetText(wsBuffTime);
        m_BuffTimeInput.SetPosition(m_Pos.x + 127, m_Pos.y + 97);
    }
    else if (m_iCurrentPage == SUB_PAGE_PARTY_CONFIG_ELF)
    {
        m_BtnPartyHeal.RegisterBoxState(_TempConfig.bAutoHealParty);

        m_BtnPartyDuration.CheckBoxInfo(m_Pos.x + 17, m_Pos.y + 168, 15, 15);
        m_BtnPartyDuration.RegisterBoxState(_TempConfig.bBuffDurationParty);
        m_iCurrentPartyHealThreshold = _TempConfig.iHealPartyThreshold / 10;

        char wsBuffTime[MAX_NUMBER_DIGITS + 1] = { 0 };
        snprintf(wsBuffTime, MAX_NUMBER_DIGITS + 1, "%d", _TempConfig.iBuffCastInterval);
        m_BuffTimeInput.SetText(wsBuffTime);
        m_BuffTimeInput.SetPosition(m_Pos.x + 127, m_Pos.y + 187);
    }

    this->Show(true);
}

void SEASON3B::CNewUIMuHelperConfig::Save()
{
    char wsNumberInput[MAX_NUMBER_DIGITS + 1]{};

    m_BuffTimeInput.GetText(wsNumberInput, sizeof(wsNumberInput));
    _TempConfig.iBuffCastInterval = CNewUIMuHelper::GetIntFromTextInput(wsNumberInput);

    _TempConfig.iPotionThreshold = m_iCurrentPotionThreshold * 10;
    _TempConfig.iHealThreshold = m_iCurrentHealThreshold * 10;
    _TempConfig.iHealPartyThreshold = m_iCurrentPartyHealThreshold * 10;
}

void SEASON3B::CNewUIMuHelperConfig::ApplySavedConfig()
{
    m_iCurrentPotionThreshold = _TempConfig.iPotionThreshold / 10;
    m_iCurrentHealThreshold = _TempConfig.iHealThreshold / 10;
    m_iCurrentPartyHealThreshold = _TempConfig.iHealPartyThreshold / 10;
}

// Called by the "Initialization" button from the main page
void SEASON3B::CNewUIMuHelperConfig::InitConfig()
{
    _TempConfig.iPotionThreshold = 40;
    _TempConfig.iHealThreshold = 60;
    _TempConfig.iBuffCastInterval = 0;
    _TempConfig.iHealPartyThreshold = 60;
    _TempConfig.bAutoHealParty = false;
    _TempConfig.bBuffDurationParty = false;
}

// Called by the "Initialization" button from the sub page
void SEASON3B::CNewUIMuHelperConfig::Reset()
{
    if (m_iCurrentPage == SUB_PAGE_SKILL2_CONFIG
        || m_iCurrentPage == SUB_PAGE_SKILL3_CONFIG)
    {
        int iSkillIndex = m_iCurrentPage == SUB_PAGE_SKILL2_CONFIG ? 1 : 2;

        _TempConfig.aiSkillCondition[iSkillIndex] = ON_MOBS_NEARBY | ON_MORE_THAN_TWO_MOBS;
        _TempConfig.aiSkillCondition[iSkillIndex] = ON_MOBS_NEARBY | ON_MORE_THAN_TWO_MOBS;

        m_BtnPreConHuntRange.RegisterBoxState(_TempConfig.aiSkillCondition[iSkillIndex] & ON_MOBS_NEARBY);
        m_BtnPreConAttacking.RegisterBoxState(_TempConfig.aiSkillCondition[iSkillIndex] & ON_MOBS_ATTACKING);
        m_BtnSubConMoreThanTwo.RegisterBoxState(_TempConfig.aiSkillCondition[iSkillIndex] & ON_MORE_THAN_TWO_MOBS);
        m_BtnSubConMoreThanThree.RegisterBoxState(_TempConfig.aiSkillCondition[iSkillIndex] & ON_MORE_THAN_THREE_MOBS);
        m_BtnSubConMoreThanFour.RegisterBoxState(_TempConfig.aiSkillCondition[iSkillIndex] & ON_MORE_THAN_FOUR_MOBS);
        m_BtnSubConMoreThanFive.RegisterBoxState(_TempConfig.aiSkillCondition[iSkillIndex] & ON_MORE_THAN_FIVE_MOBS);
    }
    else if (m_iCurrentPage == SUB_PAGE_POTION_CONFIG
        || m_iCurrentPage == SUB_PAGE_POTION_CONFIG_ELF
        || m_iCurrentPage == SUB_PAGE_POTION_CONFIG_SUMMY)
    {
        _TempConfig.iPotionThreshold = 0;
        _TempConfig.iHealThreshold = 0;

        m_iCurrentPotionThreshold = _TempConfig.iPotionThreshold / 10;
        m_iCurrentHealThreshold = _TempConfig.iHealThreshold / 10;
    }
}

NewUIMuHelperConfig.h

#pragma once

#include <array>
#include <vector>

#include "NewUIBase.h"
#include "NewUIManager.h"
#include "NewUIButton.h"
#include "MUHelper/MuHelper.h"

namespace SEASON3B
{
    class CNewUIMuHelperConfig : public CNewUIObj
    {
        public:
        CNewUIMuHelperConfig();
        ~CNewUIMuHelperConfig();

        bool Create(CNewUIManager* pNewUIMng, int x, int y);
        void Release();

        bool Render();
        bool Update();
        bool UpdateMouseEvent();
        bool UpdateKeyEvent();

        float GetLayerDepth();
        float GetKeyEventOrder();

        void Toggle(int iPage);
        void Save();
        void Reset();
        void ApplySavedConfig();
        void InitConfig();

        enum ESkillSlot
        {
            SUB_PAGE_SKILL2_CONFIG = 2,
            SUB_PAGE_SKILL3_CONFIG,
            SUB_PAGE_POTION_CONFIG_ELF,
            SUB_PAGE_POTION_CONFIG_SUMMY,
            SUB_PAGE_POTION_CONFIG,
            SUB_PAGE_PARTY_CONFIG,
            SUB_PAGE_PARTY_CONFIG_ELF
        };

        private:
        enum IMAGE_LIST
        {
            IMAGE_BASE_WINDOW_BACK = BITMAP_INTERFACE_NEW_MESSAGEBOX_BEGIN + 3,
            IMAGE_BASE_WINDOW_TOP = BITMAP_INTERFACE_NEW_PERSONALINVENTORY_BEGIN,
            IMAGE_BASE_WINDOW_LEFT = BITMAP_INTERFACE_NEW_PERSONALINVENTORY_BEGIN + 2,
            IMAGE_BASE_WINDOW_RIGHT = BITMAP_INTERFACE_NEW_PERSONALINVENTORY_BEGIN + 3,
            IMAGE_BASE_WINDOW_BOTTOM = BITMAP_INTERFACE_NEW_PERSONALINVENTORY_BEGIN + 4,
            IMAGE_BASE_WINDOW_BTN_EXIT = BITMAP_INTERFACE_NEW_PERSONALINVENTORY_BEGIN + 17,
            //--
            IMAGE_TABLE_SQUARE = BITMAP_INTERFACE_NEW_INVENTORY_BASE_BEGIN, //. newui_item_box.tga
            IMAGE_TABLE_TOP_LEFT,
            IMAGE_TABLE_TOP_RIGHT,
            IMAGE_TABLE_BOTTOM_LEFT,
            IMAGE_TABLE_BOTTOM_RIGHT,
            IMAGE_TABLE_TOP_PIXEL,
            IMAGE_TABLE_BOTTOM_PIXEL,
            IMAGE_TABLE_LEFT_PIXEL,
            IMAGE_TABLE_RIGHT_PIXEL,
            //--
            IMAGE_WINDOW_TAB_BTN = BITMAP_GUILDINFO_BEGIN,
            //--
            IMAGE_MACROUI_HELPER_RAGEMINUS = BITMAP_INTERFACE_MACROUI_BEGIN, // newui_position02.tga (70, 25)
            IMAGE_MACROUI_HELPER_OPTIONBUTTON = BITMAP_INTERFACE_MACROUI_BEGIN + 1, // newui_position02.tga (70, 25)
            IMAGE_MACROUI_HELPER_INPUTNUMBER = BITMAP_INTERFACE_MACROUI_BEGIN + 2,
            IMAGE_MACROUI_HELPER_INPUTSTRING = BITMAP_INTERFACE_MACROUI_BEGIN + 3,
            //-- Buttons
            IMAGE_CHAINFO_BTN_STAT = BITMAP_INTERFACE_NEW_CHAINFO_WINDOW_BEGIN + 1,
            IMAGE_CLEARNESS_BTN = BITMAP_CURSEDTEMPLE_BEGIN + 4,
            IMAGE_IGS_BUTTON = BITMAP_IGS_MSGBOX_BUTTON,

            IMAGE_OPTION_BTN_CHECK = BITMAP_OPTION_BEGIN + 5,
            IMAGE_OPTION_VOLUME_BACK = BITMAP_OPTION_BEGIN + 8,
            IMAGE_OPTION_VOLUME_COLOR = BITMAP_OPTION_BEGIN + 9,

        };

        static constexpr int WINDOW_WIDTH = 190;
        static constexpr int WINDOW_HEIGHT = 429;

        private:
        void InitText();
        void InitButtons();
        void SetPos(int x, int y);
        void RenderBackPane(int x, int y, int width, int height, const char* pszHeader);
        void RenderHpLevel(int x, int y, int width, int height, int level, const char* pszLabel);
        void LoadImages();
        void UnloadImages();

        CNewUIManager* m_pNewUIMng;

        POINT m_Pos;
        CNewUICheckBox m_BtnPreConHuntRange;
        CNewUICheckBox m_BtnPreConAttacking;

        CNewUICheckBox m_BtnSubConMoreThanTwo;
        CNewUICheckBox m_BtnSubConMoreThanThree;
        CNewUICheckBox m_BtnSubConMoreThanFour;
        CNewUICheckBox m_BtnSubConMoreThanFive;

        CNewUICheckBox m_BtnPartyHeal;
        CNewUICheckBox m_BtnPartyDuration;
        CUITextInputBox m_BuffTimeInput;

        CNewUIButton m_BtnSave;
        CNewUIButton m_BtnReset;
        CNewUIButton m_BtnClose;

        int m_iCurrentPage;
        int m_iCurrentHealThreshold;
        int m_iCurrentPartyHealThreshold;
        int m_iCurrentPotionThreshold;
    };

}
Bon Dia

Dakosmu

NewUIMuHelperSkillList.h

#pragma once
#include <array>
#include <vector>
#include "NewUIBase.h"
#include "NewUIManager.h"
#include "NewUIButton.h"
#include "MUHelper/MuHelper.h"
#include <_TextureIndex.h>
#include <NewUI3DRenderMng.h>

namespace SEASON3B
{
    class CNewUIMuHelperSkillList : public CNewUIObj
    {
        public:
        CNewUIMuHelperSkillList();
        ~CNewUIMuHelperSkillList();

        bool Create(CNewUIManager* pNewUIMng, CNewUI3DRenderMng* pNewUI3DRenderMng);
        void Release();

        bool UpdateMouseEvent();
        bool UpdateKeyEvent();
        bool Update();
        bool Render();
        void RenderSkillInfo();
        float GetLayerDepth();

        void Reset();

        int UpdateMouseSkillList();
        void FilterByAttackSkills();
        void FilterByBuffSkills();
        POINT SkillListPos;
        static void UI2DEffectCallback(LPVOID pClass, DWORD dwParamA, DWORD dwParamB);

    private:
        enum IMAGE_LIST
        {
            IMAGE_SKILL1 = BITMAP_INTERFACE_NEW_SKILLICON_BEGIN,
            IMAGE_SKILL2,
            IMAGE_COMMAND,
            IMAGE_SKILL3,
            IMAGE_SKILLBOX,
            IMAGE_SKILLBOX_USE,
            IMAGE_NON_SKILL1,
            IMAGE_NON_SKILL2,
            IMAGE_NON_COMMAND,
            IMAGE_NON_SKILL3,
        };

        enum EVENT_STATE
        {
            EVENT_NONE = 0,

            EVENT_BTN_HOVER_SKILLLIST,
            EVENT_BTN_DOWN_SKILLLIST,
        };

        typedef struct
        {
            int skillId;
            POINT location;
            SIZE area;
            bool isVisible;
        } cSkillIcon;

        void LoadImages();
        void UnloadImages();

        void PrepareSkillsToRender();
        void RenderSkillIcon(int iIndex, float x, float y, float width, float height);

        bool IsAttackSkill(int iSkillType);
        bool IsBuffSkill(int iSkillType);
        bool IsHealingSkill(int iSkillType);
        bool IsDefenseSkill(int iSkillType);

        std::map<int, cSkillIcon> m_skillIconMap;

        CNewUIManager* m_pNewUIMng;
        CNewUI3DRenderMng* m_pNewUI3DRenderMng;

        bool m_bFilterByAttackSkills;
        bool m_bFilterByBuffSkills;
        bool m_bRenderSkillInfo;
        int m_iRenderSkillInfoType;
        int m_iRenderSkillInfoPosX;
        int m_iRenderSkillInfoPosY;
        std::vector<int> m_aiSkillsToRender;

        EVENT_STATE m_EventState;
    };
}
Bon Dia

Dakosmu

MuHelperData.h

#pragma once

#include <cstdint>
#include <set>
#include <array>
#include <string>
#include "WSclient.h"

namespace MUHelper
{
enum ESkillActivationBase : uint32_t
{
ALWAYS = 0x00000000,
ON_TIMER = 0x00000001,
ON_CONDITION = 0x00000002,
};

enum ESkillActivationPreCon : uint32_t
{
ON_MOBS_NEARBY = 0x00000004,
ON_MOBS_ATTACKING = 0x00000008
};

enum ESkillActivationSubCon : uint32_t
{
ON_MORE_THAN_TWO_MOBS = 0x00000010,
ON_MORE_THAN_THREE_MOBS = 0x00000020,
ON_MORE_THAN_FOUR_MOBS = 0x00000040,
ON_MORE_THAN_FIVE_MOBS = 0x00000080
};

DEFINE_ENUM_FLAG_OPERATORS(ESkillActivationBase);
DEFINE_ENUM_FLAG_OPERATORS(ESkillActivationPreCon);
DEFINE_ENUM_FLAG_OPERATORS(ESkillActivationSubCon);

constexpr uint32_t MUHELPER_SKILL_PRECON_CLEAR =
~(static_cast<uint32_t>(ON_MOBS_NEARBY) |
static_cast<uint32_t>(ON_MOBS_ATTACKING));

constexpr uint32_t MUHELPER_SKILL_SUBCON_CLEAR =
~(static_cast<uint32_t>(ON_MORE_THAN_TWO_MOBS) |
static_cast<uint32_t>(ON_MORE_THAN_THREE_MOBS) |
static_cast<uint32_t>(ON_MORE_THAN_FOUR_MOBS) |
static_cast<uint32_t>(ON_MORE_THAN_FIVE_MOBS));

enum EPetAttackMode : BYTE
{
PET_ATTACK_CEASE = 0x00,
PET_ATTACK_AUTO = 0x01,
PET_ATTACK_TOGETHER = 0x02
};

typedef struct
{
int iHuntingRange = 0;

bool bLongRangeCounterAttack = false;
bool bReturnToOriginalPosition = false;
int iMaxSecondsAway = 0;

std::array<uint32_t, 3> aiSkill = { 0, 0, 0 };
std::array<uint32_t, 3> aiSkillCondition = { 0, 0, 0 };
std::array<uint32_t, 3> aiSkillInterval = { 0, 0, 0 };

bool bUseCombo = false;

std::array<uint32_t, 3> aiBuff = { 0, 0, 0 };

bool bBuffDuration = false;
bool bBuffDurationParty = false;
int iBuffCastInterval = 0;

bool bAutoHeal = false;
int iHealThreshold = 0;
bool bSupportParty = false;
bool bAutoHealParty = false;
int iHealPartyThreshold = 0;

bool bUseHealPotion = false;
int iPotionThreshold = 0;

bool bUseDrainLife = false;
bool bUseDarkRaven = false;
int iDarkRavenMode = 0;

bool bRepairItem = false;
bool StartOffline = false;

int iObtainingRange = 0;
bool bPickAllItems = false;
bool bPickSelectItems = false;
bool bPickJewel = false;
bool bPickZen = false;
bool bPickAncient = false;
bool bPickExcellent = false;
bool bPickExtraItems = false;
std::set<std::string> aExtraItems;

bool bUseSelfDefense = false;
bool bAutoAcceptFriend = false;
bool bAutoAcceptGuild = false;
bool bAutoReset = false;
} ConfigData;

class ConfigDataSerDe {
public:
static void Serialize(const ConfigData& domain, PRECEIVE_MUHELPER_DATA& packet);
static void Deserialize(const PRECEIVE_MUHELPER_DATA& packet, ConfigData& domain);
};
}

MuHelperData.cpp

#include "stdafx.h"
#include "MuHelperData.h"

namespace MUHelper
{

void ConfigDataSerDe::Serialize(const ConfigData& gameData, PRECEIVE_MUHELPER_DATA& netData)
{
memset(&netData, 0, sizeof(netData));

netData.HuntingRange = static_cast<BYTE>(gameData.iHuntingRange & 0x0F);
netData.DistanceMin = static_cast<BYTE>(gameData.iMaxSecondsAway & 0x0F);
netData.LongDistanceAttack = gameData.bLongRangeCounterAttack ? 1 : 0;
netData.OriginalPosition = gameData.bReturnToOriginalPosition ? 1 : 0;
netData.BasicSkill1 = static_cast<WORD>(gameData.aiSkill[0] & 0xFFFF);
netData.ActivationSkill1 = static_cast<WORD>(gameData.aiSkill[1] & 0xFFFF);
netData.ActivationSkill2 = static_cast<WORD>(gameData.aiSkill[2] & 0xFFFF);
netData.DelayMinSkill1 = static_cast<WORD>(gameData.aiSkillInterval[1] & 0xFFFF);
netData.DelayMinSkill2 = static_cast<WORD>(gameData.aiSkillInterval[2] & 0xFFFF);
// Skill 1 Conditions
netData.Skill1Delay = (gameData.aiSkillCondition[1] & ON_TIMER) ? 1 : 0;
netData.Skill1Con = (gameData.aiSkillCondition[1] & ON_CONDITION) ? 1 : 0;
netData.Skill1PreCon = (gameData.aiSkillCondition[1] & ON_MOBS_NEARBY) ? 0 : 1;

if (gameData.aiSkillCondition[1] & ON_MORE_THAN_TWO_MOBS)
netData.Skill1SubCon = 0;
else if (gameData.aiSkillCondition[1] & ON_MORE_THAN_THREE_MOBS)
netData.Skill1SubCon = 1;
else if (gameData.aiSkillCondition[1] & ON_MORE_THAN_FOUR_MOBS)
netData.Skill1SubCon = 2;
else if (gameData.aiSkillCondition[1] & ON_MORE_THAN_FIVE_MOBS)
netData.Skill1SubCon = 3;

// Skill 2 Conditions
netData.Skill2Delay = (gameData.aiSkillCondition[2] & ON_TIMER) ? 1 : 0;
netData.Skill2Con = (gameData.aiSkillCondition[2] & ON_CONDITION) ? 1 : 0;
netData.Skill2PreCon = (gameData.aiSkillCondition[2] & ON_MOBS_NEARBY) ? 0 : 1;

if (gameData.aiSkillCondition[2] & ON_MORE_THAN_TWO_MOBS)
netData.Skill2SubCon = 0;
else if (gameData.aiSkillCondition[2] & ON_MORE_THAN_THREE_MOBS)
netData.Skill2SubCon = 1;
else if (gameData.aiSkillCondition[2] & ON_MORE_THAN_FOUR_MOBS)
netData.Skill2SubCon = 2;
else if (gameData.aiSkillCondition[2] & ON_MORE_THAN_FIVE_MOBS)
netData.Skill2SubCon = 3;

netData.Combo = gameData.bUseCombo ? 1 : 0;

netData.BuffSkill0NumberID = static_cast<WORD>(gameData.aiBuff[0] & 0xFFFF);
netData.BuffSkill1NumberID = static_cast<WORD>(gameData.aiBuff[1] & 0xFFFF);
netData.BuffSkill2NumberID = static_cast<WORD>(gameData.aiBuff[2] & 0xFFFF);

netData.BuffDuration = gameData.bBuffDuration ? 1 : 0;
netData.BuffDurationforAllPartyMembers = gameData.bBuffDurationParty ? 1 : 0;
netData.CastingBuffMin = static_cast<WORD>(gameData.iBuffCastInterval);

netData.AutoHeal = gameData.bAutoHeal ? 1 : 0;
netData.AutoPotion = gameData.bUseHealPotion ? 1 : 0;
netData.DrainLife = gameData.bUseDrainLife ? 1 : 0;
netData.Party = gameData.bSupportParty ? 1 : 0;
netData.PreferenceOfPartyHeal = gameData.bAutoHealParty ? 1 : 0;

netData.HPStatusAutoHeal = static_cast<BYTE>((gameData.iHealThreshold / 10) & 0x0F);
netData.HPStatusAutoPotion = static_cast<BYTE>((gameData.iPotionThreshold / 10) & 0x0F);
netData.HPStatusOfPartyMembers = static_cast<BYTE>((gameData.iHealPartyThreshold / 10) & 0x0F);
netData.HPStatusDrainLife = static_cast<BYTE>((gameData.iHealThreshold / 10) & 0x0F);

netData.UseDarkSpirits = gameData.bUseDarkRaven ? 1 : 0;
netData.PetAttack = static_cast<BYTE>(gameData.iDarkRavenMode);

netData.RepairItem = gameData.bRepairItem ? 1 : 0;
netData.ObtainRange = static_cast<BYTE>(gameData.iObtainingRange & 0x0F);

netData.PickAllNearItems = gameData.bPickAllItems ? 1 : 0;
netData.PickSelectedItems = gameData.bPickSelectItems ? 1 : 0;
netData.Zen = gameData.bPickZen ? 1 : 0;
netData.JewelOrGem = gameData.bPickJewel ? 1 : 0;
netData.ExcellentItem = gameData.bPickExcellent ? 1 : 0;
netData.SetItem = gameData.bPickAncient ? 1 : 0;
netData.AddExtraItem = gameData.bPickExtraItems ? 1 : 0;

// Extra Items
memset(netData.ExtraItems, 0, sizeof(netData.ExtraItems));
int iItemIndex = 0;
for (const auto& wsItem : gameData.aExtraItems)
{
if (iItemIndex >= 12)
break;

size_t n = strnlen(strncpy(netData.ExtraItems[iItemIndex], wsItem.c_str(), 15), 15);
if (n == (size_t)-1 || n == 15)
memset(netData.ExtraItems[iItemIndex], 0, 15);

g_ConsoleDebug->Write(3, "Save ExtraItem[%d]: %s", iItemIndex, netData.ExtraItems[iItemIndex]);
iItemIndex++;
}
}


bool BoQuaItemExtr = false;
#define ON_AUTO_UPDATE 1
#if ON_AUTO_UPDATE
void ConfigDataSerDe::Deserialize(const PRECEIVE_MUHELPER_DATA& netData, ConfigData& gameData)
{
gameData.iHuntingRange = 4;
gameData.iMaxSecondsAway = 60;
gameData.bLongRangeCounterAttack = true;
gameData.bReturnToOriginalPosition = true;

gameData.aiSkill.fill(0);
gameData.aiSkill[0] = 0;
gameData.aiSkill[1] = 0;
gameData.aiSkill[2] = 0;

gameData.aiSkillInterval.fill(0);
gameData.aiSkillInterval[1] = 0;
gameData.aiSkillInterval[2] = 0;

gameData.aiSkillCondition.fill(0);
gameData.aiSkillCondition[1] = 0;
gameData.aiSkillCondition[2] = 0;

gameData.bUseCombo = false;

gameData.aiBuff.fill(0);
gameData.aiBuff[0] = 0;
gameData.aiBuff[1] = 0;
gameData.aiBuff[2] = 0;

gameData.bBuffDuration = false;
gameData.bBuffDurationParty = false;
gameData.iBuffCastInterval = 0;

gameData.bAutoHeal = false;
gameData.iHealThreshold = 0;
gameData.bUseDrainLife = false;
gameData.bUseHealPotion = true;
gameData.iPotionThreshold = 8;
gameData.bSupportParty = false;
gameData.bAutoHealParty = false;
gameData.iHealPartyThreshold = 0;

gameData.bUseDarkRaven = false;
gameData.iDarkRavenMode = 0;
gameData.bRepairItem = false;

gameData.iObtainingRange = 0;
gameData.bPickAllItems = false;
gameData.bPickSelectItems = false;
gameData.bPickZen = false;
gameData.bPickJewel = false;
gameData.bPickExcellent = false;
gameData.bPickAncient = false;
gameData.bPickExtraItems = false;

gameData.bAutoReset = CharacterAttribute->AutoResetEnable;;

if (BoQuaItemExtr)
{
char szExtraItemBuffer[15 + 1];
for (int i = 0; i < sizeof(netData.ExtraItems) / sizeof(netData.ExtraItems[0]); i++)
{
memset(szExtraItemBuffer, 0, sizeof(szExtraItemBuffer));

size_t n = strnlen(netData.ExtraItems[i], 15);
strncpy(szExtraItemBuffer, netData.ExtraItems[i], 15);

if (n > 0 && n <= 15)
{
szExtraItemBuffer[n] = '\0';
if (szExtraItemBuffer[0] != '\0')
{
gameData.aExtraItems.insert(std::string(szExtraItemBuffer));
}
}
}
}
}
#else
void ConfigDataSerDe::Deserialize(const PRECEIVE_MUHELPER_DATA& netData, ConfigData& gameData)
{
gameData.iHuntingRange = static_cast<int>(netData.HuntingRange);
g_ConsoleDebug->Write(3, "Load %d", netData.HuntingRange);
gameData.iMaxSecondsAway = static_cast<int>(netData.DistanceMin);
gameData.bLongRangeCounterAttack = (bool)netData.LongDistanceAttack;
gameData.bReturnToOriginalPosition = (bool)netData.OriginalPosition;

gameData.aiSkill.fill(0);
gameData.aiSkill[0] = static_cast<int>(netData.BasicSkill1);
gameData.aiSkill[1] = static_cast<int>(netData.ActivationSkill1);
gameData.aiSkill[2] = static_cast<int>(netData.ActivationSkill2);

gameData.aiSkillInterval.fill(0);
gameData.aiSkillInterval[1] = static_cast<int>(netData.DelayMinSkill1);
gameData.aiSkillInterval[2] = static_cast<int>(netData.DelayMinSkill2);

gameData.aiSkillCondition.fill(0);
gameData.aiSkillCondition[1] |= netData.Skill1Delay ? ON_TIMER : 0;
gameData.aiSkillCondition[1] |= netData.Skill1Con ? ON_CONDITION : 0;
gameData.aiSkillCondition[1] |= netData.Skill1PreCon == 0 ? ON_MOBS_NEARBY : ON_MOBS_ATTACKING;
gameData.aiSkillCondition[1] |= netData.Skill1SubCon == 0 ? ON_MORE_THAN_TWO_MOBS :
netData.Skill1SubCon == 1 ? ON_MORE_THAN_THREE_MOBS :
netData.Skill1SubCon == 2 ? ON_MORE_THAN_FOUR_MOBS :
netData.Skill1SubCon == 3 ? ON_MORE_THAN_FIVE_MOBS :
0;

gameData.aiSkillCondition[2] |= netData.Skill2Delay ? ON_TIMER : 0;
gameData.aiSkillCondition[2] |= netData.Skill2Con ? ON_CONDITION : 0;
gameData.aiSkillCondition[2] |= netData.Skill2PreCon == 0 ? ON_MOBS_NEARBY : ON_MOBS_ATTACKING;
gameData.aiSkillCondition[2] |= netData.Skill2SubCon == 0 ? ON_MORE_THAN_TWO_MOBS :
netData.Skill2SubCon == 1 ? ON_MORE_THAN_THREE_MOBS :
netData.Skill2SubCon == 2 ? ON_MORE_THAN_FOUR_MOBS :
netData.Skill2SubCon == 3 ? ON_MORE_THAN_FIVE_MOBS :
0;
gameData.bUseCombo = (bool)netData.Combo;

gameData.aiBuff.fill(0);
gameData.aiBuff[0] = static_cast<int>(netData.BuffSkill0NumberID);
gameData.aiBuff[1] = static_cast<int>(netData.BuffSkill1NumberID);
gameData.aiBuff[2] = static_cast<int>(netData.BuffSkill2NumberID);

gameData.bBuffDuration = (bool)netData.BuffDuration;
gameData.bBuffDurationParty = (bool)netData.BuffDurationforAllPartyMembers;
gameData.iBuffCastInterval = static_cast<int>(netData.CastingBuffMin);

gameData.bAutoHeal = (bool)netData.AutoHeal;
gameData.iHealThreshold = static_cast<int>(netData.HPStatusAutoHeal) * 10;
gameData.bUseDrainLife = static_cast<int>(netData.DrainLife);
gameData.bUseHealPotion = (bool)netData.AutoPotion;
gameData.iPotionThreshold = static_cast<int>(netData.HPStatusAutoPotion) * 10;
gameData.bSupportParty = (bool)netData.Party;
gameData.bAutoHealParty = (bool)netData.PreferenceOfPartyHeal;
gameData.iHealPartyThreshold = static_cast<int>(netData.HPStatusOfPartyMembers) * 10;

gameData.bUseDarkRaven = (bool)netData.UseDarkSpirits;
gameData.iDarkRavenMode = static_cast<int>(netData.PetAttack);
gameData.bRepairItem = (bool)netData.RepairItem;

gameData.iObtainingRange = static_cast<int>(netData.ObtainRange);
gameData.bPickAllItems = (bool)netData.PickAllNearItems;
gameData.bPickSelectItems = (bool)netData.PickSelectedItems;
gameData.bPickZen = (bool)netData.Zen;
gameData.bPickJewel = (bool)netData.JewelOrGem;
gameData.bPickExcellent = (bool)netData.ExcellentItem;
gameData.bPickAncient = (bool)netData.SetItem;
gameData.bPickExtraItems = (bool)netData.AddExtraItem;

if (BoQuaItemExtr)
{
char szExtraItemBuffer[15 + 1];
for (int i = 0; i < sizeof(netData.ExtraItems) / sizeof(netData.ExtraItems[0]); i++)
{
memset(szExtraItemBuffer, 0, sizeof(szExtraItemBuffer));

size_t n = strnlen(netData.ExtraItems[i], 15);
strncpy(szExtraItemBuffer, netData.ExtraItems[i], 15);

if (n > 0 && n <= 15)
{
szExtraItemBuffer[n] = '\0';
if (szExtraItemBuffer[0] != '\0')
{
gameData.aExtraItems.insert(std::string(szExtraItemBuffer));
}
}
}
}
}
#endif
}



Bon Dia

Dakosmu

MuHelper.cpp

#include "stdafx.h"

#include <thread>
#include <atomic>
#include <chrono>
#include <cmath>

#include "ZzzAI.h"
#include "ZzzCharacter.h"
#include "ZzzInterface.h"
#include "NewUISystem.h"
#include "Utilities/Log/muConsoleDebug.h"
#include "SkillManager.h"
#include "PartyManager.h"
#include "MapManager.h"
#include "WSclient.h"
#include "wsclientinline.h"

#include "MuHelper.h"

#include "ZzzInventory.h"
constexpr int MAX_ACTIONABLE_DISTANCE = 10;
constexpr int DEFAULT_DURABILITY_THRESHOLD = 50;

SpinLock _targetsLock;
SpinLock _itemsLock;

namespace MUHelper
{
    CMuHelper g_MuHelper;

    CMuHelper::CMuHelper()
    {
        this->ReadyPressRButton = 0;
        this->m_bForcePathMove = false;
        this->m_bActive = false;
        this->m_posOriginal = { 0, 0 };
        this->m_iCurrentItem = 0;
        this->m_iCurrentTarget = 0;
        this->m_iCurrentBuffIndex = 0;
        this->m_iCurrentBuffPartyIndex = 0;
        this->m_iCurrentHealPartyIndex = 0;
        this->m_iComboState = 0;
        this->m_iCurrentSkill = 0;
        this->m_iHuntingDistance = 0;
        this->m_iObtainingDistance = 0;
        this->m_iLoopCounter = 0;
        this->m_iSecondsElapsed = 0;
        this->m_iSecondsAway = 0;
        this->m_bTimerActivatedBuffOngoing = false;
        this->m_bPetActivated = false;
        this->m_iTotalCost = 0;

        this->m_setTargets.clear();
        this->m_setTargetsAttacking.clear();
        this->m_setItems.clear();
    }


    void CALLBACK CMuHelper::TimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
    {
        g_MuHelper.WorkLoop(hwnd, uMsg, idEvent, dwTime);
    }

    void CMuHelper::Save(const ConfigData& config)
    {
        m_config = config;

        PRECEIVE_MUHELPER_DATA netData;
        ConfigDataSerDe::Serialize(m_config, netData);
        SendMuHelperSaveDataRequest(reinterpret_cast<BYTE*>(&netData), sizeof(netData));
    }

    void CMuHelper::Load(const ConfigData& config)
    {
        m_config = config;
    }

    ConfigData CMuHelper::GetConfig() const {
        return m_config;
    }

    void CMuHelper::Toggle()
    {
        if (m_bActive)
        {
            TriggerStop();
        }
        else
        {
            TriggerStart();
        }
    }


    void CMuHelper::TriggerStart()
    {
        SendMuHelperStatusChangeRequest(0);   
    }

    void CMuHelper::TriggerStop()
    {
        SendMuHelperStatusChangeRequest(1);
    }

    void CMuHelper::Start()
    {
        if (m_bActive)
        {
            return;
        }

        m_iTotalCost = 0;
        m_iComboState = 0;
        m_iCurrentBuffIndex = 0;
        m_iCurrentBuffPartyIndex = 0;
        m_iCurrentTarget = -1;
        m_iCurrentSkill = m_config.aiSkill[0];
        m_iCurrentItem = MAX_ITEMS;
        m_posOriginal = { Hero->PositionX, Hero->PositionY };

        m_iHuntingDistance = ComputeDistanceByRange(m_config.iHuntingRange);
        m_iObtainingDistance = ComputeDistanceByRange(m_config.iObtainingRange);

        m_iSecondsElapsed = 0;
        m_iSecondsAway = 0;

        m_bTimerActivatedBuffOngoing = false;
        m_bPetActivated = false;

        m_iLoopCounter = 0;

        m_bActive = true;

        g_ConsoleDebug->Write(MCD_NORMAL, "[MU Helper] Started");
    }

    void CMuHelper::Stop()
    {
        m_bActive = false;
        g_ConsoleDebug->Write(MCD_NORMAL, "[MU Helper] Stopped");
    }

    void CMuHelper::WorkLoop(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
    {
        if (!m_bActive)
        {
            return;
        }

        if (Hero->SafeZone)
        {
            g_ConsoleDebug->Write(MCD_NORMAL, "[MU Helper] Entered safezone. Stopping.");
            TriggerStop();
            return;
        }

        Work();

        if (m_iLoopCounter++ == 4)
        {
            m_iSecondsElapsed++;

            if (ComputeDistanceBetween({ Hero->PositionX, Hero->PositionY }, m_posOriginal) > 1)
            {
                m_iSecondsAway++;
            }
            else
            {
                m_iSecondsAway = 0;
            }

            m_iLoopCounter = 0;
        }
    }

    void CMuHelper::Work()
    {
        try
        {
            if (!ActivatePet())
            {
                return;
            }

            if (!Buff())
            {
                return;
            }

            if (!RecoverHealth())
            {
                return;
            }

            if (!ObtainItem())
            {
                return;
            }

            if (!Regroup())
            {
                return;
            }

            Attack();

            RepairEquipments();
        }
        catch (...)
        {
            g_ConsoleDebug->Write(MCD_NORMAL, "[MU Helper] Exception occurred. Ignoring...");
        }
    }

    void CMuHelper::AddTarget(int iTargetId, bool bIsAttacking)
    {
        if (!m_bActive)
        {
            return;
        }

        CHARACTER* pTarget = FindCharacterByKey(iTargetId);
        if (!pTarget || pTarget == Hero)
        {
            return;
        }

        int iDistance = ComputeDistanceFromTarget(pTarget);

        if ((iDistance <= m_iHuntingDistance)
            || (bIsAttacking && m_config.bLongRangeCounterAttack))
        {
            _targetsLock.lock();

            m_setTargets.insert(iTargetId);

            if (bIsAttacking)
            {
                m_setTargetsAttacking.insert(iTargetId);
            }

            _targetsLock.unlock();
        }

        if (m_config.bUseSelfDefense)
        {
            pTarget->Object.Kind = KIND_MONSTER;
            m_iCurrentTarget = iTargetId;
        }
    }

    void CMuHelper::DeleteTarget(int iTargetId)
    {
        _targetsLock.lock();

        m_setTargets.erase(iTargetId);
        m_setTargetsAttacking.erase(iTargetId);

        _targetsLock.unlock();

        if (iTargetId == m_iCurrentTarget)
        {
            m_iCurrentTarget = -1;
        }
    }

    void CMuHelper::DeleteAllTargets()
    {
        _targetsLock.lock();

        m_setTargets.clear();
        m_setTargetsAttacking.clear();

        _targetsLock.unlock();
    }

    int CMuHelper::ComputeDistanceByRange(int iRange)
    {
        return ComputeDistanceBetween({ 0, 0 }, { iRange, iRange });
    }

    int CMuHelper::ComputeDistanceFromTarget(CHARACTER* pTarget)
    {
        POINT posA, posB;

        posA = { Hero->PositionX, Hero->PositionY };
        posB = { pTarget->PositionX, pTarget->PositionY };
        int iPrevDistance = ComputeDistanceBetween(posA, posB);

        posA = { Hero->PositionX, Hero->PositionY };
        posB = { pTarget->TargetX, pTarget->TargetX };
        int iNextDistance = ComputeDistanceBetween(posA, posB);

        return min(iPrevDistance, iNextDistance);
    }

    int CMuHelper::ComputeDistanceBetween(POINT posA, POINT posB)
    {
        int iDx = posA.x - posB.x;
        int iDy = posA.y - posB.y;

        return static_cast<int>(std::ceil(std::sqrt(iDx * iDx + iDy * iDy)));
    }

    int CMuHelper::GetNearestTarget()
    {
        int iClosestMonsterId = -1;
        int iMinDistance = m_config.iHuntingRange + 1;

        std::set<int> setTargets;
        {
            _targetsLock.lock();
            setTargets = m_setTargets;
            _targetsLock.unlock();
        }

        for (const int& iMonsterId : setTargets)
        {
            int iIndex = FindCharacterIndex(iMonsterId);
            CHARACTER* pTarget = &CharactersClient[iIndex];

            int iDistance = ComputeDistanceFromTarget(pTarget);
            if (iDistance < iMinDistance)
            {
                iMinDistance = iDistance;
                iClosestMonsterId = iMonsterId;
            }
        }

        return iClosestMonsterId;
    }

    int CMuHelper::GetFarthestAttackingTarget()
    {
        int iFarthestMonsterId = -1;
        int iMaxDistance = -1;

        std::set<int> setTargets;
        {
            _targetsLock.lock();
            setTargets = m_setTargetsAttacking;
            _targetsLock.unlock();
        }

        for (const int& iMonsterId : setTargets)
        {
            int iIndex = FindCharacterIndex(iMonsterId);
            CHARACTER* pTarget = &CharactersClient[iIndex];

            int iDistance = ComputeDistanceFromTarget(pTarget);
            if (iDistance > iMaxDistance)
            {
                iMaxDistance = iDistance;
                iFarthestMonsterId = iMonsterId;
            }
        }

        return iFarthestMonsterId;
    }

    void CMuHelper::CleanupTargets()
    {
        std::set<int> setTargets;
        {
            _targetsLock.lock();
            setTargets = m_setTargets;
            _targetsLock.unlock();
        }

        for (const int& iMonsterId : setTargets)
        {
            int iIndex = FindCharacterIndex(iMonsterId);
            if (iIndex == MAX_CHARACTERS_CLIENT)
            {
                DeleteTarget(iMonsterId);
            }

            CHARACTER* pTarget = &CharactersClient[iIndex];
            if (!pTarget || (pTarget && (pTarget->Dead > 0 || !pTarget->Object.Live)))
            {
                DeleteTarget(iMonsterId);
            }
        }
    }

    int CMuHelper::ActivatePet()
    {
        if (!m_config.bUseDarkRaven)
        {
            return 1;
        }

        if (m_bPetActivated)
        {
            return 1;
        }

        if (m_config.iDarkRavenMode == PET_ATTACK_CEASE)
        {
            SendPetCommandRequest(PET_TYPE_DARK_SPIRIT, AT_PET_COMMAND_DEFAULT, 0xFFFF);
        }
        else if (m_config.iDarkRavenMode == PET_ATTACK_AUTO)
        {
            SendPetCommandRequest(PET_TYPE_DARK_SPIRIT, AT_PET_COMMAND_RANDOM, 0xFFFF);
        }
        else if (m_config.iDarkRavenMode == PET_ATTACK_TOGETHER)
        {
            SendPetCommandRequest(PET_TYPE_DARK_SPIRIT, AT_PET_COMMAND_OWNER, 0xFFFF);
        }

        m_bPetActivated = true;
        return 1;
    }

    int CMuHelper::Buff()
    {
        if (!HasAssignedBuffSkill())
        {
            return 1;
        }

        if (m_config.bSupportParty && g_pPartyManager->IsPartyActive())
        {
            PARTY_t* pMember = &Party[m_iCurrentBuffPartyIndex];
            CHARACTER* pChar = g_pPartyManager->GetPartyMemberChar(pMember);

            if (pChar != NULL
                && pMember->Map == gMapManager.WorldActive
                && ComputeDistanceFromTarget(pChar) <= MAX_ACTIONABLE_DISTANCE)
            {
                if (!m_config.bBuffDurationParty
                    && m_config.iBuffCastInterval != 0
                    && m_iSecondsElapsed % m_config.iBuffCastInterval == 0)
                {
                    m_bTimerActivatedBuffOngoing = true;
                }

                if (!BuffTarget(pChar, m_config.aiBuff[m_iCurrentBuffIndex]))
                {
                    return 0;
                }
            }

            m_iCurrentBuffPartyIndex = (m_iCurrentBuffPartyIndex + 1) % (sizeof(Party) / sizeof(Party[0]));
        }
        else
        {
            if (!m_config.bBuffDuration
                && m_config.iBuffCastInterval != 0
                && m_iSecondsElapsed % m_config.iBuffCastInterval == 0)
            {
                m_bTimerActivatedBuffOngoing = true;
            }

            if (!BuffTarget(Hero, m_config.aiBuff[m_iCurrentBuffIndex]))
            {
                return 0;
            }
        }

        if (m_iCurrentBuffPartyIndex == 0)
        {
            m_iCurrentBuffIndex = (m_iCurrentBuffIndex + 1) % m_config.aiBuff.size();

            // Reaching this branch means everyone's been buffed,
            // so we're resetting the timer activated buff flag
            if (m_iCurrentBuffIndex == 0)
            {
                m_bTimerActivatedBuffOngoing = false;
            }
        }

        return 1;
    }

    int CMuHelper::BuffTarget(CHARACTER* pTargetChar, int iBuffSkill)
    {
        // TODO: List other buffs here
        if ((iBuffSkill == AT_SKILL_ATTACK
            || iBuffSkill == AT_SKILL_ATT_POWER_UP
            || iBuffSkill == AT_SKILL_ATT_POWER_UP + 1
            || iBuffSkill == AT_SKILL_ATT_POWER_UP + 2
            || iBuffSkill == AT_SKILL_ATT_POWER_UP + 3
            || iBuffSkill == AT_SKILL_ATT_POWER_UP + 4)
            && (!g_isCharacterBuff((&pTargetChar->Object), eBuff_Attack) || m_bTimerActivatedBuffOngoing))
        {
            return SimulateSkill(iBuffSkill, true, pTargetChar->Key);
        }

        if ((iBuffSkill == AT_SKILL_DEFENSE
            || iBuffSkill == AT_SKILL_DEF_POWER_UP
            || iBuffSkill == AT_SKILL_DEF_POWER_UP + 1
            || iBuffSkill == AT_SKILL_DEF_POWER_UP + 2
            || iBuffSkill == AT_SKILL_DEF_POWER_UP + 3
            || iBuffSkill == AT_SKILL_DEF_POWER_UP + 4)
            && (!g_isCharacterBuff((&pTargetChar->Object), eBuff_Defense) || m_bTimerActivatedBuffOngoing))
        {
            return SimulateSkill(iBuffSkill, true, pTargetChar->Key);
        }

        if ((iBuffSkill == AT_SKILL_INFINITY_ARROW) &&
            (!g_isCharacterBuff((&pTargetChar->Object), eBuff_InfinityArrow)))
        {
            return SimulateSkill(iBuffSkill, false, pTargetChar->Key);
        }

        if ((iBuffSkill == AT_SKILL_WIZARDDEFENSE
            || iBuffSkill == AT_SKILL_SOUL_UP
            || iBuffSkill == AT_SKILL_SOUL_UP + 1
            || iBuffSkill == AT_SKILL_SOUL_UP + 2
            || iBuffSkill == AT_SKILL_SOUL_UP + 3
            || iBuffSkill == AT_SKILL_SOUL_UP + 4)
            && (!g_isCharacterBuff((&pTargetChar->Object), eBuff_PhysDefense) || m_bTimerActivatedBuffOngoing))
        {
            return SimulateSkill(iBuffSkill, true, pTargetChar->Key);
        }

        if ((iBuffSkill == AT_SKILL_VITALITY
            || iBuffSkill == AT_SKILL_LIFE_UP
            || iBuffSkill == AT_SKILL_LIFE_UP + 1
            || iBuffSkill == AT_SKILL_LIFE_UP + 2
            || iBuffSkill == AT_SKILL_LIFE_UP + 3
            || iBuffSkill == AT_SKILL_LIFE_UP + 4)
            && (!g_isCharacterBuff((&pTargetChar->Object), eBuff_HpRecovery) || m_bTimerActivatedBuffOngoing))
        {
            if (m_iComboState == 2)
            {
                return 1;
            }

            return SimulateSkill(iBuffSkill, false, pTargetChar->Key);
        }

        if ((iBuffSkill == AT_SKILL_SWELL_OF_MAGICPOWER || iBuffSkill == MASTER_SKILL_ADD_MAGIC_CIRCLE_IMPROVED|| iBuffSkill == MASTER_SKILL_ADD_MAGIC_CIRCLE_ENHANCED)
            && (!g_isCharacterBuff((&pTargetChar->Object), eBuff_SwellOfMagicPower)))
        {
            return SimulateSkill(iBuffSkill, false, pTargetChar->Key);
        }

        if ((iBuffSkill == AT_SKILL_ADD_CRITICAL)
            && (!g_isCharacterBuff((&pTargetChar->Object), eBuff_AddCriticalDamage)))
        {
            return SimulateSkill(iBuffSkill, false, pTargetChar->Key);
        }

        if ((iBuffSkill == AT_SKILL_ALICE_BERSERKER)
            && (!g_isCharacterBuff((&pTargetChar->Object), eBuff_Berserker)))
        {
            return SimulateSkill(iBuffSkill, false, pTargetChar->Key);
        }
        if ((iBuffSkill == AT_SKILL_ALICE_THORNS)
            && (!g_isCharacterBuff((&pTargetChar->Object), eBuff_Thorns)))
        {
            return SimulateSkill(iBuffSkill, false, pTargetChar->Key);
        }

        return 1;
    }

    int CMuHelper::ConsumePotion()
    {
        int64_t iLife = CharacterAttribute->ViewCurHP;
        int64_t iLifeMax = CharacterAttribute->ViewMaxHP;

        if (m_config.bUseHealPotion && iLifeMax > 0 && iLife > 0)
        {
            int64_t iRemaining = (iLife * 100 + iLifeMax - 1) / iLifeMax;
            if (iRemaining <= m_config.iPotionThreshold)
            {
                int iPotionIndex = g_pMyInventory->FindHealingItemIndex();
                if (iPotionIndex != -1)
                {
                    SendRequestUse(iPotionIndex, 0);
                }
            }
        }

        return 1;
    }

    int CMuHelper::RecoverHealth()
    {
        if (!Heal())
        {
            return 0;
        }
       
        if (!DrainLife())
        {
            return 0;
        }

        if (!ConsumePotion())
        {
            return 0;
        }

        return 1;
    }

    int CMuHelper::Heal()
    {
        if (!m_config.bAutoHeal)
        {
            return 1;
        }

        int iHealingSkill = GetHealingSkill();
        if (iHealingSkill == -1)
        {
            return 1;
        }

        if (m_config.bAutoHealParty && g_pPartyManager->IsPartyActive())
        {
            PARTY_t* pMember = &Party[m_iCurrentHealPartyIndex];
            CHARACTER* pChar = g_pPartyManager->GetPartyMemberChar(pMember);

            if (pChar != NULL)
            {
                if (pChar == Hero)
                {
                    return HealSelf(iHealingSkill);
                }
                else if (pMember->Map == gMapManager.WorldActive
                    && pMember->stepHP * 10 <= m_config.iHealPartyThreshold
                    && ComputeDistanceFromTarget(pChar) <= MAX_ACTIONABLE_DISTANCE)
                {
                    return SimulateSkill(iHealingSkill, true, pChar->Key);
                }
            }
            m_iCurrentHealPartyIndex = (m_iCurrentHealPartyIndex + 1) % (sizeof(Party) / sizeof(Party[0]));
        }
        else
        {
            return HealSelf(iHealingSkill);
        }

        return 1;
    }

    int CMuHelper::HealSelf(int iHealingSkill)
    {
        int64_t iLife = CharacterAttribute->ViewCurHP;
        int64_t iLifeMax = CharacterAttribute->ViewMaxHP;
        int64_t iRemaining = (iLife * 100 + iLifeMax - 1) / iLifeMax;

        if (iRemaining <= m_config.iHealThreshold)
        {
            return SimulateSkill(iHealingSkill, true, HeroKey);
        }

        return 1;
    }

    int CMuHelper::DrainLife()
    {
        if (!m_config.bUseDrainLife)
        {
            return 1;
        }

        int iDrainLife = GetDrainLifeSkill();
        if (iDrainLife == -1)
        {
            return 1;
        }

        int64_t iLife = CharacterAttribute->Life;
        int64_t iLifeMax = CharacterAttribute->LifeMax;
        int64_t iRemaining = (iLife * 100 + iLifeMax - 1) / iLifeMax;

        if (iRemaining <= m_config.iHealThreshold)
        {
            m_iCurrentTarget = GetNearestTarget();
            if (m_iCurrentTarget != -1)
            {
                return SimulateSkill(iDrainLife, true, m_iCurrentTarget);
            }
        }

        return 1;
    }

    int CMuHelper::RepairEquipments()
    {
        if (m_config.bRepairItem)
        {
            for (int i = 0; i < MAX_EQUIPMENT; i++)
            {
                ITEM* pItem = &CharacterMachine->Equipment[i];
                if (!pItem || pItem->Type == -1)
                {
                    continue;
                }

                ITEM_ATTRIBUTE* pAttr = &ItemAttribute[pItem->Type];
                if (!pAttr)
                {
                    continue;
                }

                int iLevel = pItem->Level;
                int iDurability = pItem->Durability;
                int iMaxDurability = calcMaxDurability(pItem, pAttr, iLevel);

                int64_t iHealth = (iDurability * 100 + iMaxDurability - 1) / iMaxDurability;

                if (iHealth <= 50)
                {
                    int64_t iGoldCost = CalcSelfRepairCost(ItemValue(pItem, 2), iDurability, iMaxDurability, pItem->Type);
                    if (iGoldCost <= CharacterMachine->Gold)
                    {
                        SendRequestRepair(i, 1);
                    }
                }
            }
        }

        return 1;
    }

    int CMuHelper::Attack()
    {
        if (m_iCurrentTarget == -1)
        {
            if (!m_setTargets.empty())
            {
                CleanupTargets();

                if (m_config.bLongRangeCounterAttack)
                {
                    m_iCurrentTarget = GetFarthestAttackingTarget();
                }
               
                if (m_iCurrentTarget == -1)
                {
                    m_iCurrentTarget = GetNearestTarget();
                }
            }
            else
            {
                m_iComboState = 0;
                return 0;
            }
        }

        if (m_config.bUseCombo)
        {
            return SimulateComboAttack();
        }

        m_iCurrentSkill = SelectAttackSkill();
        if (m_iCurrentSkill > 0)
        {
            SimulateAttack(m_iCurrentSkill);
        }

        return 1;
    }

    int CMuHelper::SelectAttackSkill()
    {
        // try skill 2 activation conditions
        if (m_config.aiSkill[1] > 0 && m_config.aiSkill[1] < MAX_SKILLS)
        {
            if ((m_config.aiSkillCondition[1] & ON_TIMER)
                && m_config.aiSkillInterval[1] != 0
                && m_iSecondsElapsed % m_config.aiSkillInterval[1] == 0)
            {
                return m_config.aiSkill[1];
            }

            if (m_config.aiSkillCondition[1] & ON_CONDITION)
            {
                if (m_config.aiSkillCondition[1] & ON_MOBS_NEARBY)
                {
                    int iCount = m_setTargets.size();

                    if (((m_config.aiSkillCondition[1] & ON_MORE_THAN_TWO_MOBS) && iCount >= 2)
                        || ((m_config.aiSkillCondition[1] & ON_MORE_THAN_THREE_MOBS) && iCount >= 3)
                        || ((m_config.aiSkillCondition[1] & ON_MORE_THAN_FOUR_MOBS) && iCount >= 4)
                        || ((m_config.aiSkillCondition[1] & ON_MORE_THAN_FIVE_MOBS) && iCount >= 5))
                    {
                        return m_config.aiSkill[1];
                    }
                }
                else if (m_config.aiSkillCondition[1] & ON_MOBS_ATTACKING)
                {
                    int iCount = m_setTargetsAttacking.size();

                    if (((m_config.aiSkillCondition[1] & ON_MORE_THAN_TWO_MOBS) && iCount >= 2)
                        || ((m_config.aiSkillCondition[1] & ON_MORE_THAN_THREE_MOBS) && iCount >= 3)
                        || ((m_config.aiSkillCondition[1] & ON_MORE_THAN_FOUR_MOBS) && iCount >= 4)
                        || ((m_config.aiSkillCondition[1] & ON_MORE_THAN_FIVE_MOBS) && iCount >= 5))
                    {
                        return m_config.aiSkill[1];
                    }
                }
            }
        }

        // try skill 3 activation conditions
        if (m_config.aiSkill[2] > 0 && m_config.aiSkill[2] < MAX_SKILLS)
        {
            if ((m_config.aiSkillCondition[2] & ON_TIMER)
                && m_config.aiSkillInterval[2] != 0
                && m_iSecondsElapsed % m_config.aiSkillInterval[2] == 0)
            {
                return m_config.aiSkill[2];
            }

            if (m_config.aiSkillCondition[2] & ON_CONDITION)
            {
                if (m_config.aiSkillCondition[2] & ON_MOBS_NEARBY)
                {
                    int iCount = m_setTargets.size();

                    if (((m_config.aiSkillCondition[2] & ON_MORE_THAN_TWO_MOBS) && iCount >= 2)
                        || ((m_config.aiSkillCondition[2] & ON_MORE_THAN_THREE_MOBS) && iCount >= 3)
                        || ((m_config.aiSkillCondition[2] & ON_MORE_THAN_FOUR_MOBS) && iCount >= 4)
                        || ((m_config.aiSkillCondition[2] & ON_MORE_THAN_FIVE_MOBS) && iCount >= 5))
                    {
                        return m_config.aiSkill[2];
                    }
                }
                else if (m_config.aiSkillCondition[2] & ON_MOBS_ATTACKING)
                {
                    int iCount = m_setTargetsAttacking.size();

                    if (((m_config.aiSkillCondition[2] & ON_MORE_THAN_TWO_MOBS) && iCount >= 2)
                        || ((m_config.aiSkillCondition[2] & ON_MORE_THAN_THREE_MOBS) && iCount >= 3)
                        || ((m_config.aiSkillCondition[2] & ON_MORE_THAN_FOUR_MOBS) && iCount >= 4)
                        || ((m_config.aiSkillCondition[2] & ON_MORE_THAN_FIVE_MOBS) && iCount >= 5))
                    {
                        return m_config.aiSkill[2];
                    }
                }
            }
        }

        // no skill for activation yet, default to basic skill
        if (m_config.aiSkill[0] > 0)
        {
            return m_config.aiSkill[0];
        }

        return -1;
    }

    int CMuHelper::SimulateComboAttack()
    {
        for (int i = 0; i < m_config.aiSkill.size(); i++)
        {
            if (m_config.aiSkill[i] == 0)
            {
                return 0;
            }
        }

        if (SimulateAttack(m_config.aiSkill[m_iComboState]))
        {
            m_iComboState = (m_iComboState + 1) % 3;
        }

        return 1;
    }

    int CMuHelper::SimulateAttack(int iSkill)
    {
        return SimulateSkill(iSkill, true, m_iCurrentTarget);
    }

    int CMuHelper::SimulateSkill(int iSkill, bool bTargetRequired, int iTarget)
    {
        extern MovementSkill g_MovementSkill;
        extern int SelectedCharacter;
        extern int TargetX, TargetY;

        g_MovementSkill.m_iSkill = iSkill;
        g_MovementSkill.m_bMagic = true;

        float fSkillDistance = gSkillManager.GetSkillDistance(iSkill, Hero);

        if (bTargetRequired)
        {
            if (iTarget == -1)
            {
                return 0;
            }

            SelectedCharacter = FindCharacterIndex(iTarget);
            if (SelectedCharacter == MAX_CHARACTERS_CLIENT)
            {
                DeleteTarget(iTarget);
                return 0;
            }

            CHARACTER* pTarget = &CharactersClient[SelectedCharacter];
            if (pTarget->Dead > 0)
            {
                DeleteTarget(iTarget);
                return 0;
            }

            g_MovementSkill.m_iTarget = SelectedCharacter;

            TargetX = (int)(pTarget->Object.Position[0] / TERRAIN_SCALE);
            TargetY = (int)(pTarget->Object.Position[1] / TERRAIN_SCALE);

            PATH_t tempPath;
            bool bHasPath = PathFinding2(Hero->PositionX, Hero->PositionY, TargetX, TargetY, &tempPath, m_iHuntingDistance + fSkillDistance);
            bool bTargetNear = CheckTile(Hero, &Hero->Object, fSkillDistance);
            bool bNoWall = CheckWall(Hero->PositionX, Hero->PositionY, TargetX, TargetY);

            // target not reachable, ignore it
            if (!bHasPath)
            {
                DeleteTarget(iTarget);
                return 0;
            }

            // target is not near or the path is obstructed by a wall, move closer
            if (!bTargetNear || !bNoWall)
            {
                Hero->Path.Lock.lock();

                // Limit movement to 2 steps at a time
                int pathNum = min(tempPath.PathNum, 2);
                for (int i = 0; i < pathNum; i++)
                {
                    Hero->Path.PathX[i] = tempPath.PathX[i];
                    Hero->Path.PathY[i] = tempPath.PathY[i];
                }
                Hero->Path.PathNum = pathNum;
                Hero->Path.CurrentPath = 0;
                Hero->Path.CurrentPathFloat = 0;

                Hero->Path.Lock.unlock();

                SendMove(Hero, &Hero->Object);
                return 0;
            }
        }
        else
        {
            TargetX = Hero->PositionX;
            TargetY = Hero->PositionY;
        }

        int iSkillResult = ExecuteSkill(Hero, iSkill, fSkillDistance);
        if (iSkillResult == -1)
        {
            DeleteTarget(iTarget);
        }

        return (int)(iSkillResult == 1);
    }

    int CMuHelper::Regroup()
    {
        if (m_config.bReturnToOriginalPosition && m_iSecondsAway > m_config.iMaxSecondsAway)
        {
            if (!SimulateMove(m_posOriginal))
            {
                return 0;
            }

            m_iSecondsAway = 0;
            m_iComboState = 0;
            m_iCurrentTarget = -1;
        }

        return 1;
    }

    int CMuHelper::SimulateMove(POINT posMove)
    {
        extern int TargetX, TargetY;

        Hero->MovementType = MOVEMENT_MOVE;
        TargetX = (int)posMove.x;
        TargetY = (int)posMove.y;

        if (!CheckTile(Hero, &Hero->Object, 1.5f))
        {
            if (PathFinding2((Hero->PositionX), (Hero->PositionY), TargetX, TargetY, &Hero->Path))
            {
                SendMove(Hero, &Hero->Object);
            }
            return 0;
        }

        return 1;
    }

    bool CMuHelper::HasAssignedBuffSkill()
    {
        for (int i = 0; i < m_config.aiBuff.size(); i++)
        {
            if (m_config.aiBuff[i] != 0)
            {
                return true;
            }
        }

        return false;
    }

    int CMuHelper::GetHealingSkill()
    {
        std::vector<int> aiHealingSkills =
        {
            AT_SKILL_HEAL_UP,
            AT_SKILL_HEAL_UP + 1,
            AT_SKILL_HEAL_UP + 2,
            AT_SKILL_HEAL_UP + 3,
            AT_SKILL_HEAL_UP + 4,
            AT_SKILL_HEALING
        };

        for (int i = 0; i < aiHealingSkills.size(); i++)
        {
            int iSkillIndex = g_pSkillList->GetSkillIndex(aiHealingSkills[i]);
            if (iSkillIndex != -1)
            {
                return aiHealingSkills[i];
            }
        }

        return -1;
    }

    int CMuHelper::GetDrainLifeSkill()
    {
        std::vector<int> aiDrainLifeSkills =
        {
            AT_SKILL_ALICE_DRAINLIFE_UP,
            AT_SKILL_ALICE_DRAINLIFE_UP + 1,
            AT_SKILL_ALICE_DRAINLIFE_UP + 2,
            AT_SKILL_ALICE_DRAINLIFE_UP + 3,
            AT_SKILL_ALICE_DRAINLIFE_UP + 4,
            AT_SKILL_ALICE_DRAINLIFE,
        };

        for (int i = 0; i < aiDrainLifeSkills.size(); i++)
        {
            int iSkillIndex = g_pSkillList->GetSkillIndex(aiDrainLifeSkills[i]);
            if (iSkillIndex != -1)
            {
                return aiDrainLifeSkills[i];
            }
        }

        return -1;
    }

    int CMuHelper::ObtainItem()
    {
        if (m_iCurrentItem == MAX_ITEMS)
        {
            m_iCurrentItem = SelectItemToObtain();
            if (m_iCurrentItem == MAX_ITEMS)
            {
                return 1;
            }
        }

        ITEM_t* pDrop = &Items[m_iCurrentItem];
        ITEM* pItem = &pDrop->Item;

        if (!pDrop->Object.Live)
        {
            DeleteItem(m_iCurrentItem);
            return 1;
        }

        extern int TargetX;
        extern int TargetY;

        TargetX = (int)(Items[m_iCurrentItem].Object.Position[0] / TERRAIN_SCALE);
        TargetY = (int)(Items[m_iCurrentItem].Object.Position[1] / TERRAIN_SCALE);

        int iDistance = ComputeDistanceBetween({ Hero->PositionX, Hero->PositionY }, { TargetX, TargetY });
        if (iDistance <= m_iObtainingDistance)
        {
            if (!CheckTile(Hero, &Hero->Object, 1.5f))
            {
                if (PathFinding2((Hero->PositionX), (Hero->PositionY), TargetX, TargetY, &Hero->Path))
                {
                    SendMove(Hero, &Hero->Object);
                }

                return 0;
            }
            else
            {
               SendPickupItemRequest(m_iCurrentItem);
                DeleteItem(m_iCurrentItem);
            }
        }

        return 1;
    }

    bool CMuHelper::ShouldObtainItem(int iItemId)
    {
        ITEM_t* pDrop = &Items[iItemId];
        ITEM* pItem = &pDrop->Item;

        if (m_config.bPickAllItems)
        {
            return true;
        }

        if (!m_config.bPickSelectItems)
        {
            return false;
        }

        if ((m_config.bPickZen && IsMoneyItem(pItem)) ||
            (m_config.bPickJewel && IsJewelItem(pItem)) ||
            (m_config.bPickAncient && pItem->ExtOption) ||
            (m_config.bPickExcellent && pItem->Option1)
           
            )
        {
            return true;
        }

        if (m_config.bPickExtraItems)
        {
            std::string strDisplayName = GetItemDisplayName(pItem);
            for (const auto& str : m_config.aExtraItems)
            {
                if (strDisplayName.find(str) != std::string::npos)
                {
                    return true;
                }
            }
        }

        return false;
    }


    void CMuHelper::AddItem(int iItemId, POINT posWhere)
    {
        _itemsLock.lock();
        m_setItems.insert(iItemId);
        _itemsLock.unlock();
    }

    void CMuHelper::DeleteItem(int iItemId)
    {
        _itemsLock.lock();
        m_setItems.erase(iItemId);
        _itemsLock.unlock();

        if (iItemId == m_iCurrentItem)
        {
            m_iCurrentItem = MAX_ITEMS;
        }
    }

    int CMuHelper::SelectItemToObtain()
    {
        int iClosestItemId = MAX_ITEMS;
        int iMinDistance = m_config.iObtainingRange + 1;

        std::set<int> setItems;
        {
            _itemsLock.lock();
            setItems = m_setItems;
            _itemsLock.unlock();
        }

        for (const int& iItemId : setItems)
        {
            if (!ShouldObtainItem(iItemId))
            {
                continue;
            }

            int iItemX = (int)(Items[iItemId].Object.Position[0] / TERRAIN_SCALE);
            int iItemY = (int)(Items[iItemId].Object.Position[1] / TERRAIN_SCALE);

            int iDistance = ComputeDistanceBetween({ Hero->PositionX, Hero->PositionY }, { iItemX, iItemY });
            if (iDistance < iMinDistance)
            {
                iMinDistance = iDistance;
                iClosestItemId = iItemId;
            }
        }

        return iClosestItemId;
    }

    void CMuHelper::SimulateMoveToPos(POINT pos)
    {
        if (!Hero || !Hero->Object.Live || !m_bActive)
            return;

        m_posOriginal = pos;

        m_iSecondsAway = m_config.iMaxSecondsAway + 1;

        m_iCurrentTarget = -1;
    }
}

MuHelper.h
[code]#pragma once

#include <functional>
#include <array>
#include <set>
#include <string>
#include <thread>
#include <atomic>

#include "MuHelperData.h"

namespace MUHelper
{
   class CMuHelper
   {
      public:
      CMuHelper();
      ~CMuHelper() = default;

      static void CALLBACK TimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime);

      ConfigData GetConfig() const;
      void Save(const ConfigData& config);
      void Load(const ConfigData& config);
      void Start();
      void Stop();
      void Toggle();
      void TriggerStart();
      void TriggerStop();
      bool IsActive() { return m_bActive; }
      void AddCost(int iCost) { m_iTotalCost += iCost; }
      int GetTotalCost() { return m_iTotalCost; }

      void AddTarget(int iTargetId, bool bIsAttacking);
      void DeleteTarget(int iTargetId);
      void DeleteAllTargets();

      void AddItem(int iItemId, POINT posDropped);
      void Del
Bon Dia

SPK


Dakosmu

Bon Dia

🡱 🡳
Real Time Web Analytics