• You've discovered RedGuides 📕 an EverQuest multi-boxing community 🛡️🧙🗡️. We want you to play several EQ characters at once, come join us and say hello! 👋
  • IS THIS SITE UGLY? Change the look. To dismiss this notice, click the X --->
Resource icon

Unmaintained MQ2Headshot

Other Authors
Naes
Included in Very Vanilla
Very Vanilla Included! No need to download.
Server Type
🏢 Live 🏘️ Emu TLP Test
NOT MAINTAINED

Version 1.2 - Added /headcount command. Need help finding indexes for other classes to optimize the plugin. (see: MQ2Aaindex)

OxD3r.png


Version 1.1 - Works for Paladin slay undead, Berserker decapitation, Rogue anatomy/assassinate, and Ranger headshot.

Sony likes to troll us by putting a bunch of mobs that can't actually be headshot in all the good camps, whether because they are not humanoid, or because they are too high for the max rank. Level 90's in Kaesora Library know what I'm talking about, but I have had this problem pretty much everywhere I've gone.

Here is a plugin I wrote to make it blatantly obvious if a mob can be headshot so you don't waste your time. When clearing camps, you can skip the ones that are too high level easily.

MZBDa.png


PHP:
// MQ2Headshot.cpp : Defines the entry point for the DLL application.
// Author: Naes
#include "../MQ2Plugin.h"

PreSetup("MQ2Headshot");
PLUGIN_VERSION(1.2);

#define HEADSHOT_HUMANOID 1
#define HEADSHOT_UNDEAD 3
#define HEADSHOT_ANYTHING -1

// can leave as-is in case other classes get similar in future
PCHAR szHeadshotLabel[] = {
    "", // 0x0
    "", // Warrior
    "", // Cleric
    "UNDEAD", // Paladin
    "HEADSHOT", // Ranger
    "", // Shadow Knight
    "", // Druid
    "", // Monk
    "", // Bard
    "ASSASSINATE", // Rogue
    "", // Shaman
    "", // Necromancer
    "", // Wizard
    "", // Magician
    "", // Enchanter
    "", // Beastlord
    "DECAPITATE" // Berserker
};

PSPAWNINFO pCharFix;
int showHeadCount = -1;
char INISection[MAX_STRING];

int HeadshotStandardFormula(int startLevel, _ALTABILITY* aa, int defaultLevel = 0)
{
    return (aa) ? (startLevel - 2) + (aa->AARankRequired * 2) : defaultLevel;
}

bool CanHeadshot(PSPAWNINFO pNewSpawn)
{
    /*static const/**/ DWORD headshotAAIndex = GetAAIndexByName("Headshot"); //  = 13573;
    /*static const/**/ DWORD anatomyAAIndex = GetAAIndexByName("Anatomy");
    /*static const/**/ DWORD slayUndeadAAIndex = GetAAIndexByName("Slay Undead");
    /*static const/**/ DWORD decapitationAAIndex = GetAAIndexByName("Decapitation");

    if (GetSpawnType(pNewSpawn) == NPC &&
         gGameState==GAMESTATE_INGAME) // not at char select
    {
        _ALTABILITY* aa = NULL;
        int bodyType = HEADSHOT_HUMANOID;
    
        int maxKillLevel = 0; // set to highest headshotable, label all under it

        if (GetCharInfo() && GetCharInfo()->pSpawn)
            pCharFix =  GetCharInfo()->pSpawn;

        switch (pCharFix->Class)
        {
            case Ranger:
                aa = pAltAdvManager->GetAltAbility(headshotAAIndex);
                maxKillLevel = HeadshotStandardFormula(46, aa);  //rk1=46, rk22=88
            break;

            case Rogue:
                aa = pAltAdvManager->GetAltAbility(anatomyAAIndex);
                if (pCharFix->Level >= 60)
                    maxKillLevel = HeadshotStandardFormula(46, aa, 44); //rk0=44, rk1=46, rk22=88
            break;

            case Paladin:
                bodyType = HEADSHOT_UNDEAD;
                aa = pAltAdvManager->GetAltAbility(slayUndeadAAIndex);
                if (aa)
                    maxKillLevel = 255; // rk1=??
            break;

            case Berserker:
                bodyType = HEADSHOT_ANYTHING;
                aa = pAltAdvManager->GetAltAbility(decapitationAAIndex);
                maxKillLevel = HeadshotStandardFormula(46, aa);  //rk1=84, rk3=88
            break;
        }

        if ((bodyType == HEADSHOT_ANYTHING || GetBodyType(pNewSpawn) == bodyType) &&
            pNewSpawn->Level <= maxKillLevel)
                return true;
    }

    return false;
}

PLUGIN_API VOID OnAddSpawn(PSPAWNINFO pNewSpawn)
{
    if (CanHeadshot(pNewSpawn))
    {
        char new_name[MAX_STRING];
        sprintf(new_name, "%s: %d", szHeadshotLabel[pCharFix->Class], pNewSpawn->Level);
        strcpy(pNewSpawn->Lastname, new_name);
    }
}

void WriteHeadcountSetting(int activate)
{
    CHAR szMsg[MAX_STRING]={0};
    CHAR szTemp[MAX_STRING]={0};
 
    sprintf(szTemp, "%d", activate);

    showHeadCount = activate;
    WritePrivateProfileString(INISection, "ShowHeadcount", szTemp, INIFileName);

    sprintf(szMsg, "[MQ2Headshot] /headcount setting: %d", activate);
    WriteChatColor(szMsg, USERCOLOR_DEFAULT);
}

void HeadcountCommand(PSPAWNINFO pChar, PCHAR szLine)
{
    if (strlen(szLine) != 0)
    {
        CHAR Arg1[MAX_STRING] = {0};
        GetArg(Arg1, szLine, 1);

        if (!stricmp(Arg1, "off"))
        {
            WriteHeadcountSetting(0);
            return;
        } else if (!stricmp(Arg1, "on"))
        {
            WriteHeadcountSetting(1);
        }
    }

    PSPAWNINFO pSpawns = NULL;
    if (ppSpawnManager && pSpawnList)
    pSpawns = (PSPAWNINFO)pSpawnList;

    unsigned int count = 0;
    while (pSpawns)
    {
        if (CanHeadshot(pSpawns))
            ++count;

        pSpawns = pSpawns->pNext;
    }
 
    char buffer[MAX_STRING];
    sprintf(buffer, "[MQ2Headshot] # of victims in %s: %d", ((PZONEINFO)pZoneInfo)->ShortName, count);
    WriteChatColor(buffer, USERCOLOR_DEFAULT);
}

PLUGIN_API VOID SetGameState(DWORD newGameState)
{
    DebugSpewAlways("MQ2Headshot::SetGameState(%d)", newGameState);
    // fix for first load
    if (newGameState == GAMESTATE_INGAME)
    {
        if (showHeadCount == -1)
        {
            if (!pCharFix && GetCharInfo() && GetCharInfo()->pSpawn)
                pCharFix = GetCharInfo()->pSpawn;
            sprintf(INISection,"%s_%s", pCharFix->Name, EQADDR_SERVERNAME);
            showHeadCount = GetPrivateProfileInt(INISection, "ShowHeadcount", 1, INIFileName);

            PSPAWNINFO pNewSpawns = NULL;
            if (ppSpawnManager && pSpawnList)
                pNewSpawns = (PSPAWNINFO)pSpawnList;

            while (pNewSpawns)  // clear the lastnames
            {
                OnAddSpawn(pNewSpawns);

                pNewSpawns = pNewSpawns->pNext;
            }
        }
 
        if (showHeadCount == 1)
            HeadcountCommand(pCharFix, "");
 
    } else if (newGameState == GAMESTATE_CHARSELECT)
    {
        showHeadCount = -1;
    }
}

PLUGIN_API VOID InitializePlugin(VOID)
{
    if (GetCharInfo() && GetCharInfo()->pSpawn)
        pCharFix = GetCharInfo()->pSpawn;
 
    AddCommand("/headcount", HeadcountCommand);

    WriteChatColor("[MQ2Headshot] /headcount [on/off/current]", USERCOLOR_DEFAULT);
}

// Called once, when the plugin is to shutdown
PLUGIN_API VOID ShutdownPlugin(VOID)
{
    DebugSpewAlways("Shutting down MQ2Headshot");

    PSPAWNINFO pClearSpawns = NULL;
    if (ppSpawnManager && pSpawnList)
        pClearSpawns = (PSPAWNINFO)pSpawnList;

    while (pClearSpawns)  // clear the lastnames
    {
        if (CanHeadshot(pClearSpawns))
            strcpy(pClearSpawns->Lastname, "");

        pClearSpawns = pClearSpawns->pNext;
    }

 
    RemoveCommand("/headcount");
}
Source Repository
https://github.com/RedGuides/MQ2Headshot
[git] Automation options?
Yes
Author
Naes
First release
Last update
Rating
0.00 star(s) 0 ratings

Share this resource

Top