Esse script é para consertar um bug comum em servidores 97d, que não possui muita influência em servidores fáceis e medianos, mas sim, em servidores hards.

Um video de demonstração do bug:


Segue abaixo:
Skill.h:
Código:
#pragma once

namespace Network
{
    struct PMSG_USEITEM
    {
        PBMSG_HEAD h;
        unsigned char Pos;
        unsigned char Target;
        unsigned char Type;
    };

    struct SkillStruct
    {
        unsigned short    Item;
        unsigned short    Level;
        unsigned short    Strength;
        unsigned short    Dexterity;
        unsigned short    Energy;
         unsigned char    Class[4];
    };

    class Skill
    {
    public:
        static bool Init();
        static void Register(PMSG_USEITEM* lpMsg, DWORD dwIndex);
    
    public:
        static SkillStruct Struct[30];
    };
}
Skill.cpp:
Código:
#include "Skill.h"

namespace Network
{
    SkillStruct Skill::Struct[30];

    bool Skill::Init()
    {
        DarKTeaM::TokenizerEx _skill("..\\Data\\Lang\\Kor\\Item(kor).txt");

        if(_skill.Loaded)
        {
            if(!_skill.SectionExists(12) || !_skill.SectionExists(15))
            {
                return false;
            }
            else
            {
                for(int i = 0; i < 30; i++)
                {
                    Struct[i].Item = 0;
                }

                for(int i = 7, n = 0; i < 20; i++, n++)
                {
                    if(i == 15)
                    {
                        continue;
                    }

                    Struct[n].Item        = ITEMGET(12, i);
                    Struct[n].Level        = (unsigned short)_skill[12][i][10].ToInteger();
                    Struct[n].Strength    = (unsigned short)_skill[12][i][12].ToInteger();
                    Struct[n].Dexterity    = (unsigned short)_skill[12][i][13].ToInteger();
                    Struct[n].Energy    = (unsigned short)_skill[12][i][11].ToInteger();
                    Struct[n].Class[0]    = (unsigned char)_skill[12][i][15].ToInteger();
                    Struct[n].Class[1]    = (unsigned char)_skill[12][i][16].ToInteger();
                    Struct[n].Class[2]    = (unsigned char)_skill[12][i][17].ToInteger();
                    Struct[n].Class[3]    = (unsigned char)_skill[12][i][18].ToInteger();
                }

                for(int i = 0, n = 13; i < 16; i++, n++)
                {
                    Struct[n].Item        = ITEMGET(15, i);
                    Struct[n].Level        = (unsigned short)_skill[15][i][8].ToInteger();
                    Struct[n].Strength    = 0;
                    Struct[n].Dexterity    = 0;
                    Struct[n].Energy    = (unsigned short)_skill[15][i][9].ToInteger();
                    Struct[n].Class[0]    = (unsigned char)_skill[15][i][11].ToInteger();
                    Struct[n].Class[1]    = (unsigned char)_skill[15][i][12].ToInteger();
                    Struct[n].Class[2]    = (unsigned char)_skill[15][i][13].ToInteger();
                    Struct[n].Class[3]    = (unsigned char)_skill[15][i][14].ToInteger();
                }

                return true;
            }
        }

        return false;
    }

    void Skill::Register(PMSG_USEITEM* lpMsg, DWORD dwIndex)
    {
        BYTE Pos = lpMsg->Pos;
        bool szProceed = true;

        if(Object[dwIndex].pInventory[Pos].m_Type >= ITEMGET(12, 7) && Object[dwIndex].pInventory[Pos].m_Type <= ITEMGET(12, 14) || Object[dwIndex].pInventory[Pos].m_Type >= ITEMGET(12, 16) && Object[dwIndex].pInventory[Pos].m_Type <= ITEMGET(12, 19) || Object[dwIndex].pInventory[Pos].m_Type >= ITEMGET(15, 0))
        {
            for(BYTE i = 0; i < 30; i++)
            {
                if(Struct[i].Item == 0)
                {
                    break;
                }
            
                if(Object[dwIndex].pInventory[Pos].m_Type == Struct[i].Item)
                {
                    if(Object[dwIndex].Level < Struct[i].Level)
                    {
                        szProceed = false;
                    }
                    else if(Object[dwIndex].Strength < Struct[i].Strength)
                    {
                        szProceed = false;
                    }
                    else if(Object[dwIndex].Agility < Struct[i].Dexterity)
                    {
                        szProceed = false;
                    }
                    else if(Object[dwIndex].Energy < Struct[i].Energy)
                    {
                        szProceed = false;
                    }
                    else
                    {
                        BYTE szClass, szRequire[2];
                    
                        szClass         = Struct[i].Class[Object[dwIndex].Class];
                        szRequire[0] = (Object[dwIndex].Class == 0) ? 0 : (Object[dwIndex].Class == 1) ? 16 : (Object[dwIndex].Class == 2) ? 32 : 48;
                        szRequire[1] = (Object[dwIndex].Class == 0) ? 1 : (Object[dwIndex].Class == 1) ? 17 : (Object[dwIndex].Class == 2) ? 33 : 48;

                        if(szClass == 1)
                        {
                            if(Object[dwIndex].DbClass != szRequire[0] && Object[dwIndex].DbClass != szRequire[1])
                            {
                                szProceed = false;
                            }
                        }
                        else if(szClass == 2)
                        {
                            if(Object[dwIndex].DbClass != szRequire[1])
                            {
                                szProceed = false;
                            }
                        }
                        else
                        {
                            szProceed = false;
                        }
                    }

                    break;
                }
            }
        }

        if(!szProceed)
        {
            ServerReFillSend(dwIndex, (WORD)Object[dwIndex].Life, 0xFD, 1);
            return;
        }

        ServerUseItemRecv(lpMsg, dwIndex);
    }
}
Hook:
Código:
Hook((DWORD)&Network::Skill::Register, 0x4012BC);
Defines:
Código:
#define ServerUseItemRecv        ((void(*)(Network::PMSG_USEITEM*, DWORD)) 0x427A70)
#define ServerReFillSend        ((void(*)(DWORD, WORD, BYTE, BYTE)) 0x428FD0)
Função Tokenizer (Créditos totalmente do WoLf (DarK TeaM), fiz pequenas adptações para a leitura do item(kor).txt [Lembrando, é necessário usar a library boost, disponíveis para downloads aqui: [Somente usuários registrados podem vem os links. ]):
Tokenizer.h:
Código:
#pragma once

#include <map>
#include <iostream>
#include <fstream>
#include <string>
#include <boost/tokenizer.hpp>
#include <boost/algorithm/string.hpp>

namespace DarKTeaM
{
    class TokenizerField
    {
    protected:
        std::string Value;

    public:
        TokenizerField();
        TokenizerField(std::string Val);

        int    ToInteger();
        long ToLong();
        double ToDouble();
        float ToFloat();
        std::string ToString();
        char* ToStringPtr();

    };

    class TokenizerRow
    {
    public:
        std::map<int, TokenizerField>    Fields;
        int                                    FieldCount;

    public:
        TokenizerRow();
        TokenizerRow(std::string strLine);

        TokenizerField& operator[] (int FieldIndex);

        char*        GetField(int FieldIndex, char* Default);
        std::string    GetField(int FieldIndex, std::string Default);
        float        GetField(int FieldIndex, float Default);
        int            GetField(int FieldIndex, int Default);
        long        GetField(int FieldIndex, long Default);
        double        GetField(int FieldIndex, double Default);

        bool FieldExists(int FieldIndex);

    };

    class TokenizerSection
    {
    public:
        std::map<int, TokenizerRow>        Rows;
        int                                RowCount;

    public:
        TokenizerSection();
        TokenizerRow&    operator[] (int RowIndex);
        bool RowExists(int RowIndex);

    };

    class Tokenizer
    {
    public:
        std::map<int, TokenizerSection>    Sections;
        bool                                    Loaded;

    public:
        Tokenizer(std::string szFile);
        TokenizerSection& operator[](int SectionIndex);
        bool SectionExists(int SectionIndex);

    };

    TokenizerField::TokenizerField() {
    this->Value = "";
    }
    TokenizerField::TokenizerField(std::string Val) {
        this->Value = Val;
    }
    int    TokenizerField::ToInteger() {
        return atoi(this->Value.c_str());
    }
    long TokenizerField::ToLong() {
        return atol(this->Value.c_str());
    }
    double TokenizerField::ToDouble() {
        return atof(this->Value.c_str());
    }
    float TokenizerField::ToFloat() {
        return (float)(atof(this->Value.c_str()));
    }
    std::string TokenizerField::ToString() {
        return this->Value;
    }
    char* TokenizerField::ToStringPtr() {
        return (char*)this->Value.c_str();
    }

    TokenizerRow::TokenizerRow()
    {
        this->FieldCount = 0;
    }

    TokenizerField& TokenizerRow::operator [](int FieldIndex)
    {
        return this->Fields[FieldIndex];
    }

    char* TokenizerRow::GetField (int FieldIndex, char* Default)
    {
        if(!this->FieldExists(FieldIndex)) return Default;
        return this->Fields[FieldIndex].ToStringPtr();
    }

    std::string TokenizerRow::GetField (int FieldIndex, std::string Default)
    {
        if(!this->FieldExists(FieldIndex)) return Default;
        return this->Fields[FieldIndex].ToString();
    }

    float TokenizerRow::GetField (int FieldIndex, float Default)
    {
        if(!this->FieldExists(FieldIndex)) return Default;
        return this->Fields[FieldIndex].ToFloat();
    }

    int TokenizerRow::GetField (int FieldIndex, int Default)
    {
        if(!this->FieldExists(FieldIndex)) return Default;
        return this->Fields[FieldIndex].ToInteger();
    }

    long TokenizerRow::GetField (int FieldIndex, long Default)
    {
        if(!this->FieldExists(FieldIndex)) return Default;
        return this->Fields[FieldIndex].ToLong();
    }

    double TokenizerRow::GetField (int FieldIndex, double Default)
    {
        if(!this->FieldExists(FieldIndex)) return Default;
        return this->Fields[FieldIndex].ToDouble();
    }


    TokenizerRow::TokenizerRow(std::string strLine)
    {

        this->FieldCount = 0;

        std::string sep1 = "\\";
        std::string sep2 = " \t";
        std::string sep3 = "\"";
        boost::escaped_list_separator<char> sep(sep1, sep2, sep3);
        boost::tokenizer<boost::escaped_list_separator<char> > tok(strLine, sep);

        for (boost::tokenizer<boost::escaped_list_separator<char> >::iterator it = tok.begin();
            it != tok.end(); ++it)
        {
            if(*it == "") continue;
            this->Fields[this->FieldCount++] = TokenizerField(std::string(*it));
        }

    }


    bool TokenizerRow::FieldExists(int FieldIndex)
    {
        std::map<int, TokenizerField>::iterator it = this->Fields.find(FieldIndex);
        return (bool)(it != this->Fields.end());
    }

    TokenizerSection::TokenizerSection()
    {
        this->Rows.clear();
        this->RowCount = 0;
    }
    TokenizerRow& TokenizerSection::operator[] (int RowIndex)
    {
        return this->Rows[RowIndex];
    }

    bool TokenizerSection::RowExists(int RowIndex)
    {
        std::map<int, TokenizerRow>::iterator it = this->Rows.find(RowIndex);
        return (bool)(it != this->Rows.end());
    }

    TokenizerEx::TokenizerEx(std::string szFile)
    {
        this->Loaded = false;
        std::fstream f(szFile.c_str(), std::ios::in);
    
        if(f.is_open() && f.good())
        {
            TokenizerSection sec = TokenizerSection();
            
            int CurrentSection = 0;
            int RowIndex = 0;
            bool SectionOpen = false;

            while(!f.eof())
            {
                char temp[4096];
                char* dump = NULL;
                std::string line = "";
                ZeroMemory(&temp[0], 4096);
                f.getline(&temp[0], 4095);
                line.assign(&temp[0]);
                dump = (char*)line.c_str();

                bool finishstart = false;
                bool finishend = false;

                while(true)
                {
                    if(line.length() > 0)
                    {
                        if(line.at(0) == ' ' || line.at(0) == '\t')    
                        {
                            line.erase(0, 1);
                        }
                        else
                        {
                            if(finishend)
                            {
                                break;
                            }

                            finishstart = true;
                        }
                    }
                    else
                    {
                        break;
                    }

                    if(line.length() > 0)
                    {
                        if(line.at((line.length() - 1)) == ' ' || line.at((line.length() - 1)) == '\t')
                        {
                            line.erase((line.length() - 1), 1);
                        }
                        else
                        {
                            if(finishstart)
                            {
                                break;
                            }

                            finishend = true;
                        }
                    }
                    else
                    {
                        break;
                    }
                }

                if(line.substr(0, 2) == "//" || line.substr(0, 1) == "#" || line.substr(0, 1) == ";" || line == "")    
                {
                    continue;
                }

                TokenizerRow row(line);

                if(!SectionOpen)
                {
                    if(row.FieldCount == 1)
                    {
                        if(row[0].ToString().length() < 3)
                        {
                            //MessageBox(0, "Abrindo seção", "Seção", MB_OK);
                            CurrentSection = row[0].ToInteger();
                            SectionOpen = true;
                            RowIndex = 0;
                        }
                    }
                }
                else
                {
                    if(row.FieldCount == 1)
                    {
                        if(row[0].ToString() == "end" && SectionOpen)
                        {
                            //MessageBox(0, "Fechando", "Seção", MB_OK);
                            this->Sections[CurrentSection] = sec;
                            sec = TokenizerSection();
                            SectionOpen = false;
                            continue;
                        }
                    }
                    
                    sec[RowIndex++] = row;
                    sec.RowCount++;
                }
            }

            this->Loaded = true;
        }
    }

    TokenizerSection& TokenizerEx::operator[](int SectionIndex)
    {
        return this->Sections[SectionIndex];
    }

    bool TokenizerEx::SectionExists(int SectionIndex)
    {
        std::map<int, TokenizerSection>::iterator it = this->Sections.find(SectionIndex);
        return (bool)(this->Sections.end() != it);
    }
}

Créditos:
Network.