All credit to Psycotic for the changes
Changed plugin commands to /showlink so it doesnt interfear with self Linkdb. This plugin has been modified to respond to tells with the command
!link itemname. Also note command /showlink itemname will send itemlist to your last tell. Stuff to do add Masterlist and Channel support.
Changed plugin commands to /showlink so it doesnt interfear with self Linkdb. This plugin has been modified to respond to tells with the command
!link itemname. Also note command /showlink itemname will send itemlist to your last tell. Stuff to do add Masterlist and Channel support.
Code:
// MQ2LinkDB2
//
// Version 1.1 - 20th Sep 2005 - Originally by Ziggy, then updated for DoD
//
// Personal link database
//
// Usage: /Showlink - Display statistics
// /Showlink /import - Import items.txt as downloaded from 13th Floor
// (Unzip the items.txt file to the mq2\release directory)
// /Showlink /max n - Set number of maximum displayed results (default 10)
// /Showlink search - Find items containg search string
//
// Item links are displayed as tells from Linkdb in normal chat windows. This is
// so you can use the links given. MQ2's ChatWnd removes links.
// They do not go to log file. Nor will you get the Linkdb name pop up when you hit
// your reply button.
//
// Incoming chat is scanned for links, and the database is checked for this item.
// If it's not in database, it will be added.
//
// Changes:
// 1.1 Updated to reflect new link format. Thanks to ksmith and Nilwean
// and topside from
// http://eqitems.13th-floor.org/phpBB2/viewtopic.php?t=145
//
// 1.06 Alpha sorts the list
// Makes sure that if an exact match is found, then it's displayed, even if we've
// already displayed max results
// Only searches the name for the item name, previously searched the whole link
// (eg: EB41 would have matched 2 items from their hash key)
//
// 1.05 Fixed the really stupid mistake with item bitfield. And found out a new equation:
// Moved items.txt import into plugin (You can still use it external if you want)
//
// 1.04 Added Max item list so we don't get too spammed by results.
//
// 1.03 Added some more clues for debugging purposes (do: /Showlink /!quiet to show)
//
// 1.02 Fixed file locking fudge up. Should now add items to database properly.
//
// 1.01 Added item index so we know already which items the DB has to save a bunch
// of time with checking when adding new items
//
// TODO:
// Auto update from 13th floor website
//
//
#include "../MQ2Plugin.h"
#define MY_STRING "MQ2LinkDB \ar1.1\ax by Ziggy"
PreSetup("MQ2LinkDB");
static int ConvertItemsDotTxt (void);
static bool bQuietMode = true; // Display debug chatter?
static int iAddedThisSession = 0; // How many new links found since startup
static int iTotalInDB = 0; // Number of links in db
static bool bKnowTotal = false; // Do we know how many links in db?
static int iMaxResults = 10; // Display at most this many results
#define MAX_PRESENT 100000
static unsigned char * abPresent = NULL; // Bitfield to say yes/no we have/don't have each item id
#define MAX_INTERNAL_RESULTS 500 // Max size of our sort array
#define SORTEM
char TellSource[MAX_STRING];
char szTemp[MAX_STRING];
char MyMaster[MAX_STRING];
//
// static void SaveSettings (void)
//
static void SaveSettings (void)
{
char cFilename[MAX_PATH];
sprintf(cFilename,"%s\\MQ2LinkDB.ini", gszINIPath);
char cNumber[16];
itoa (iMaxResults, cNumber, 10);
WritePrivateProfileString ("Settings", "MaxResults", cNumber, cFilename);
}
//
// static void LoadSettings (void)
//
static void LoadSettings (void)
{
char cFilename[MAX_PATH];
sprintf(cFilename,"%s\\MQ2LinkDB.ini", gszINIPath);
iMaxResults = GetPrivateProfileInt ("Settings", "MaxResults", 10, cFilename);
if (iMaxResults < 1) iMaxResults = 1;
}
//
// static int ItemID (const char * cLink)
//
static int ItemID (const char * cLink)
{
char cMid[6];
// Skip \x12 and firt number
memcpy (cMid, cLink + 2, 5);
cMid[5] = '\0';
return (int) (strtol (cMid, NULL, 16));
}
//
// void CreateIndex (void)
//
void CreateIndex (void)
{
if (abPresent != NULL) {
return;
}
// TODO: Make this dynamic size
abPresent = new unsigned char[MAX_PRESENT / 8 + 1];
memset (abPresent, 0, MAX_PRESENT / 8 + 1);
char cFilename[MAX_PATH];
sprintf(cFilename,"%s\\MQ2LinkDB.txt", gszINIPath);
FILE * File = fopen (cFilename, "rt");
iTotalInDB = 0;
bKnowTotal = true;
if (File != NULL) {
char cLine[1024];
while (fgets (cLine, 1024, File) != NULL) {
int iItemID = ItemID (cLine);
if (iItemID >= MAX_PRESENT) {
WriteChatf ("MQ2LinkDB: ERROR! Make max index size bigger. (Max: %d, ItemID: %d)", MAX_PRESENT, iItemID);
continue;
}
unsigned char cOR = 1 << (iItemID % 8);
abPresent[iItemID / 8] |= cOR;
iTotalInDB++;
}
fclose (File);
}
}
//
// static bool IsAuged (const char * cLink)
// Make sure no augs in the link
//
static bool IsAuged (const char * cLink)
{
char cMid[6];
for (int i = 0; i < 5; i++) {
memcpy (cMid, cLink + 7 + (i * 5), 5);
cMid[5] = '\0';
if (atoi (cMid) != 0) {
return (true);
}
}
return (false);
}
//
// static bool FindLink (const char * cLink)
//
static bool FindLink (const char * cLink)
{
int iItemID = ItemID (cLink);
if (abPresent != NULL) {
unsigned char cOR = 1 << (iItemID % 8);
if ((abPresent[iItemID / 8] & cOR) != 0) {
if (!bQuietMode) {
WriteChatf ("MQ2LinkDB: Saw link \ay%d\ax, but we already have it.", iItemID);
}
return (true);
}
}
// Since we're scanning the file anyway, we'll make the index here to save some time, and to
// account for ppl creating new MQ2LinkDB.txt files or whatever.
if (abPresent == NULL) {
abPresent = new unsigned char[MAX_PRESENT / 8 + 1];
}
memset (abPresent, 0, MAX_PRESENT / 8 + 1);
bool bRet = false;
char cFilename[MAX_PATH];
sprintf(cFilename,"%s\\MQ2LinkDB.txt", gszINIPath);
FILE * File = fopen (cFilename, "rt");
if (File != NULL) {
char cLine[1024];
while (fgets (cLine, 1024, File) != NULL) {
cLine[strlen (cLine) - 1] = '\0'; // No LF pls
if (ItemID (cLine) == iItemID) {
unsigned char cOR = 1 << (iItemID % 8);
abPresent[iItemID / 8] |= cOR;
bRet = true;
if (IsAuged (cLine) && !IsAuged (cLink)) {
if (strlen (cLine) == strlen (cLink)) {
long lPos = ftell (File);
fclose (File);
if (!bQuietMode) {
WriteChatf ("MQ2LinkDB: Replacing auged link with un-auged link for item \ay%d\ax", iItemID);
}
File = fopen (cFilename, "r+");
if (File != NULL) {
fseek (File, lPos - strlen (cLine) - 2, SEEK_SET);
// Double check position - paranoia!
char cTemp[10];
fread (cTemp, 10, 1, File);
if (memcmp (cTemp, cLink, 8) == 0) {
fseek (File, lPos - strlen (cLine) - 2, SEEK_SET); // Seek same place again
fwrite (cLink, strlen (cLink), 1, File);
} else {
if (!bQuietMode) {
WriteChatf ("MQ2LinkDB: \arERROR - Sanity check failed while replacing");
}
}
fclose (File);
} else {
if (!bQuietMode) {
WriteChatf ("MQ2LinkDB: \arERROR - Could not open db file for writing (%d)", errno);
}
}
return (true);
}
} else {
if (!bQuietMode) {
WriteChatf ("MQ2LinkDB: Saw link \ay%d\ax, but we already have it.", iItemID);
}
}
}
}
fclose (File);
}
return (bRet);
}
//
// static void StoreLink (const char * cLink)
//
static void StoreLink (const char * cLink)
{
char cFilename[MAX_PATH];
sprintf(cFilename,"%s\\MQ2LinkDB.txt", gszINIPath);
FILE * File = fopen (cFilename, "at");
if (File != NULL) {
fputs (cLink, File);
fputs ("\n", File);
iAddedThisSession++;
iTotalInDB++;
CreateIndex (); // Won't create if it's already there
if (abPresent != NULL) {
int iItemID = ItemID (cLink);
unsigned char cOR = 1 << (iItemID % 8);
abPresent[iItemID / 8] |= cOR;
}
if (!bQuietMode) {
WriteChatf ("MQ2LinkDB: Stored link for item ID: \ay%d\ax (\ay%d\ax stored this session)", ItemID (cLink), iAddedThisSession);
}
fclose (File);
} else {
if (!bQuietMode) {
WriteChatf ("MQ2LinkDB: \arERROR - Could not open db file for writing (%d)", errno);
}
}
}
//
// static char * LinkExtract (char * cLink)
//
static char * LinkExtract (char * cLink)
{
char * cTemp = strdup (cLink);
char * cEnd = strchr (cTemp + 46, '\x12');
int iLen = 1;
if (cEnd != NULL) {
*(cEnd + 1) = '\0';
iLen = strlen (cTemp);
if (!FindLink (cTemp)) {
StoreLink (cTemp);
}
}
free (cTemp);
return (cLink + iLen);
}
//
// static void ChatTell (char * cLine)
//
static void ChatTell (char * cLine)
{
char cTemp[1024];
sprintf(cTemp, ";tell %s %s",TellSource, cLine);
DoCommand(GetCharInfo()->pSpawn, cTemp);
}
//
// static void DoParameters (PCHAR cParams)
//
static void DoParameters (PCHAR cParams)
{
bool bAnyParams = false;
strlwr (cParams);
char * cWord = strtok (cParams, " ");
while (cWord != NULL) {
if (strcmp (cWord, "/quiet") == 0) {
bQuietMode = !bQuietMode;
WriteChatf ("MQ2LinkDB: Quiet mode \ay%s\ax", bQuietMode ? "on" : "off");
bAnyParams = true;
} else if (strcmp (cWord, "/max") == 0) {
cWord = strtok (NULL, " ");
if (atoi (cWord) > 0) {
iMaxResults = atoi (cWord);
WriteChatf ("MQ2LinkDB: Max results now \ay%d\ax", iMaxResults);
SaveSettings ();
}
bAnyParams = true;
} else if (strcmp (cWord, "/import") == 0) {
ConvertItemsDotTxt ();
bAnyParams = true;
}
cWord = strtok (NULL, " ");
}
if (!bAnyParams) {
WriteChatf (MY_STRING);
WriteChatf ("MQ2LinkDB: Syntax: \ay/Showlink [/import] [/max n] [search string]\ax");
if (bKnowTotal) {
WriteChatf ("MQ2LinkDB: \ay%d\ax links in database, \ay%d\ax links added this session", iTotalInDB, iAddedThisSession);
} else {
WriteChatf ("MQ2LinkDB: \ay%d\ax links added this session", iAddedThisSession);
}
WriteChatf ("MQ2LinkDB: Max Results \ay%d\ax", iMaxResults);
}
}
//
// static VOID CommandLink (PSPAWNINFO pChar, PCHAR szline)
//
static VOID CommandLink (PSPAWNINFO pChar, PCHAR szline)
{
if (strlen (szline) < 3) {
DoParameters ("");
return; // We don't list entire DB. that's just not funny
}
if (szline[0] == '/' || szline[0] == '-') {
DoParameters (szline);
return;
}
bool bRet = false;
char cTemp[1024];
if (abPresent == NULL) {
abPresent = new unsigned char[MAX_PRESENT / 8 + 1];
}
memset (abPresent, 0, MAX_PRESENT / 8 + 1);
int iFound = 0, iTotal = 0;
char cFilename[MAX_PATH];
sprintf(cFilename,"%s\\MQ2LinkDB.txt", gszINIPath);
FILE * File = fopen (cFilename, "rt");
if (File != NULL) {
WriteChatf ("MQ2LinkDB: Searching for '\ay%s\ax'...", szline);
strlwr (szline);
char cLine[256];
char cCopy[256];
char ** cArray = new char * [MAX_INTERNAL_RESULTS];
char ** cArrayPtr = cArray;
memset (cArray, 0, sizeof (char *) * MAX_INTERNAL_RESULTS);
while (fgets (cLine, sizeof (cLine), File) != NULL) {
int iItemID = ItemID (cLine);
unsigned char cOR = 1 << (iItemID % 8);
abPresent[iItemID / 8] |= cOR;
cLine[strlen (cLine) - 1] = '\0'; // No LF pls
strcpy (cCopy, cLine);
strlwr (cCopy);
if (strstr (cCopy + 46, szline) != NULL) {
if (iFound < MAX_INTERNAL_RESULTS) {
*cArrayPtr = strdup (cLine);
cArrayPtr++;
}
iFound++;
}
iTotal++;
}
bKnowTotal = true;
iTotalInDB = iTotal;
fclose (File);
bool bForcedExtra = false;
int iMaxLoop = (iFound > MAX_INTERNAL_RESULTS ? MAX_INTERNAL_RESULTS : iFound);
if (iFound > 0) {
// Sort the list
for (int j = 0; j < iMaxLoop; j++) {
for (int i = 0; i < iMaxLoop - 1; i++) {
if (strcmp (cArray[i] + 46, cArray[i + 1] + 46) > 0) {
char * cTemp = cArray[i];
cArray[i] = cArray[i + 1];
cArray[i + 1] = cTemp;
}
}
}
// Show list
for (int i = 0; i < iMaxLoop; i++) {
bool bShow = i < iMaxResults;
char cTemp[256];
strcpy (cTemp, cArray[i]);
if (IsAuged (cArray[i])) {
strcat (cTemp, " (Augmented)");
}
if (strlen (cArray[i] + 47) == strlen (szline) && memicmp (cArray[i] + 46, szline, strlen (szline)) == 0) {
strcat (cTemp, " <Exact Match>");
bShow = true; // We display this result even if we've already shown iMaxResults count
bForcedExtra = i >= iMaxResults;
}
if (bShow) {
ChatTell (cTemp);
}
free (cArray[i]);
cArray[i] = NULL;
}
}
free (cArray);
char cTemp3[128];
sprintf (cTemp3, "MQ2LinkDB: Found \ay%d\ax items from database of \ay%d\ax total items", iFound, iTotal);
sprintf (cTemp, "Found %d items from database of %d total items", iFound, iTotal);
if (iFound > iMaxResults) {
char cTemp2[64];
sprintf (cTemp2, " - \arList capped to \ay%d\ar results", iMaxResults);
strcat (cTemp3, cTemp2);
sprintf (cTemp2, " - List capped to %d results", iMaxResults);
strcat (cTemp, cTemp2);
if (bForcedExtra) {
strcat (cTemp, " plus exact match");
strcat (cTemp3, " plus exact match");
}
}
WriteChatf (cTemp3);
ChatTell (cTemp);
} else {
WriteChatf ("MQ2LinkDB: No item database yet");
}
}
// Called once, when the plugin is to initialize
PLUGIN_API VOID InitializePlugin(VOID)
{
DebugSpewAlways("Initializing MQ2LinkDB");
AddCommand("/Showlink",CommandLink);
LoadSettings ();
abPresent = NULL;
}
// Called once, when the plugin is to shutdown
PLUGIN_API VOID ShutdownPlugin(VOID)
{
DebugSpewAlways("Shutting down MQ2LinkDB");
RemoveCommand("/Showlink");
free (abPresent);
abPresent = NULL;
}
// This is called every time EQ shows a line of chat with CEverQuest::dsp_chat,
// but after MQ filters and chat events are taken care of.
PLUGIN_API DWORD OnIncomingChat(PCHAR Line, DWORD Color)
{
DebugSpewAlways("MQ2LinkDB::OnIncomingChat(%s)",Line);
//
// Find a link from a tell
//
if (strstr (Line, "tells you,")) {
GetArg(TellSource,Line,1);
char * cTemp = strdup (Line);
char * cRequest = strtok (cTemp, "'");
cRequest = strtok (NULL, "'"); // Should containt their input string
if (memicmp (cRequest, "!link ", 6) == 0) { // Did they type !link <something>?
CommandLink(NULL, cRequest + 6); // Do it
}
free (cTemp);
}
// End linkbot tell
char *cPtr = Line;
while (*cPtr) {
if (*cPtr == '\x12') {
cPtr = LinkExtract(cPtr);
} else {
cPtr++;
}
}
return 0;
}
// DB Converter now part of plugin
typedef struct {
int itemclass;
char name[64];
char lore[64];
char idfile[16];
unsigned long id;
int weight;
short norent;
short nodrop;
short size;
short slots;
long price;
short icon;
short UNK012;
short UNK013;
int benefitflag;
short tradeskills;
short cr;
short dr;
short pr;
short mr;
short fr;
short astr;
short asta;
short aagi;
short adex;
short acha;
short aint;
short awis;
short hp;
short mana;
short ac;
short deity;
short UNK033;
short skillmodvalue;
short skillmodtype;
short banedmgrace;
short banedmgamt;
short banedmgbody;
short magic;
short casttime_;
short reqlevel;
short bardtype;
short bardvalue;
short light;
short delay;
short reclevel;
short recskill;
short elemdmgtype;
short elemdmgamt;
short range;
short damage;
short color;
short classes;
short races;
short UNK053;
short maxcharges;
short itemtype;
short material;
short sellrate;
short UNK058;
short casttime;
short UNK060;
short procrate;
short combateffects;
short shielding;
short stunresist;
short strikethrough;
short extradmgskill;
short extradmgamt;
short spellshield;
short avoidance;
short accuracy;
short charmfileid;
short factionmod1;
short factionmod2;
short factionmod3;
short factionmod4;
short factionamt1;
short factionamt2;
short factionamt3;
short factionamt4;
short charmfile;
short augtype;
short augslot1type;
short augslot1unk;
short augslot2type;
short augslot2unk;
short augslot3type;
short augslot3unk;
short augslot4type;
short augslot4unk;
short augslot5type;
short augslot5unk;
short ldontheme;
short ldonprice;
short ldonsold;
short bagtype;
short bagslots;
short bagsize;
short bagwr;
short book;
short booktype;
short filename;
short banedmgraceamt;
short augrestrict;
short loregroup;
short pendingloreflag;
short artifactflag;
short summonedflag;
short favor;
short fvnodrop;
short endur;
short dotshielding;
short attack;
short regen;
short manaregen;
short endregen;
short haste;
short damageshield;
short recastdelay;
short recasttype;
short guildfavor;
short augdistiller;
short UNK123;
short UNK124;
short attuneable;
short nopet;
short UNK127;
short pointtype;
short potionbelt;
short UNK130;
short stacksize;
short notransfer;
short clickeffect;
short clicktype;
short clicklevel2;
short clicklevel;
short UNK137;
short proceffect;
short proctype;
short proclevel2;
short proclevel;
short UNK142;
short worneffect;
short worntype;
short wornlevel2;
short wornlevel;
short UNK147;
short focuseffect;
short focustype;
short focuslevel2;
short focuslevel;
short UNK152;
short scrolleffect;
short scrolltype;
short scrolllevel2;
short scrolllevel;
short UNK157;
short verified;
} EQITEM, * PEQITEM;
//
// void SetField (PEQITEM Item, int iField, const char * cField)
//
void SetField (PEQITEM Item, int iField, const char * cField)
{
int iInt = atoi (cField);
bool bBool = iInt > 0;
switch (iField) {
case 0: Item->itemclass = iInt; break;
case 1: strcpy (Item->name, cField); break;
case 2: strcpy (Item->lore, cField); break;
case 3: strcpy (Item->idfile, cField); break;
case 4: Item->id = iInt; break;
case 5: Item->weight = iInt; break;
case 6: Item->norent = iInt; break;
case 7: Item->nodrop = iInt; break;
case 8: Item->size = iInt; break;
case 9: Item->slots = iInt; break;
case 10: Item->price = iInt; break;
case 11: Item->icon = iInt; break;
case 12: Item->UNK012 = iInt; break;
case 13: Item->UNK013 = iInt; break;
case 14: Item->benefitflag = iInt; break;
case 15: Item->tradeskills = bBool;
case 16: Item->cr = iInt; break;
case 17: Item->dr = iInt; break;
case 18: Item->pr = iInt; break;
case 19: Item->mr = iInt; break;
case 20: Item->fr = iInt; break;
case 21: Item->astr = iInt; break;
case 22: Item->asta = iInt; break;
case 23: Item->aagi = iInt; break;
case 24: Item->adex = iInt; break;
case 25: Item->acha = iInt; break;
case 26: Item->aint = iInt; break;
case 27: Item->awis = iInt; break;
case 28: Item->hp = iInt; break;
case 29: Item->mana = iInt; break;
case 30: Item->ac = iInt; break;
case 31: Item->deity = iInt; break;
case 32: Item->UNK033 = iInt; break;
case 33: Item->skillmodvalue = iInt; break;
case 34: Item->skillmodtype = iInt; break;
case 35: Item->banedmgrace = iInt; break;
case 36: Item->banedmgamt = iInt; break;
case 37: Item->banedmgbody = iInt; break;
case 38: Item->magic = iInt; break;
case 39: Item->casttime_ = iInt; break;
case 40: Item->reqlevel = iInt; break;
case 41: Item->bardtype = iInt; break;
case 42: Item->bardvalue = iInt; break;
case 43: Item->light = iInt; break;
case 44: Item->delay = iInt; break;
case 45: Item->reclevel = iInt; break;
case 46: Item->recskill = iInt; break;
case 47: Item->elemdmgtype = iInt; break;
case 48: Item->elemdmgamt = iInt; break;
case 49: Item->range = iInt; break;
case 50: Item->damage = iInt; break;
case 51: Item->color = iInt; break;
case 52: Item->classes = iInt; break;
case 53: Item->races = iInt; break;
case 54: Item->UNK053 = iInt; break;
case 55: Item->maxcharges = iInt; break;
case 56: Item->itemtype = iInt; break;
case 57: Item->material = iInt; break;
case 58: Item->sellrate = iInt; break;
case 59: Item->UNK058 = iInt; break;
case 60: Item->casttime = iInt; break;
case 61: Item->UNK060 = iInt; break;
case 62: Item->procrate = iInt; break;
case 63: Item->combateffects = iInt; break;
case 64: Item->shielding = iInt; break;
case 65: Item->stunresist = iInt; break;
case 66: Item->strikethrough = iInt; break;
case 67: Item->extradmgskill = iInt; break;
case 68: Item->extradmgamt = iInt; break;
case 69: Item->spellshield = iInt; break;
case 70: Item->avoidance = iInt; break;
case 71: Item->accuracy = iInt; break;
case 72: Item->charmfileid = iInt; break;
case 73: Item->factionmod1 = iInt; break;
case 74: Item->factionmod2 = iInt; break;
case 75: Item->factionmod3 = iInt; break;
case 76: Item->factionmod4 = iInt; break;
case 77: Item->factionamt1 = iInt; break;
case 78: Item->factionamt2 = iInt; break;
case 79: Item->factionamt3 = iInt; break;
case 80: Item->factionamt4 = iInt; break;
case 81: Item->charmfile = iInt; break;
case 82: Item->augtype = iInt; break;
case 83: Item->augslot1type = iInt; break;
case 84: Item->augslot1unk = iInt; break;
case 85: Item->augslot2type = iInt; break;
case 86: Item->augslot2unk = iInt; break;
case 87: Item->augslot3type = iInt; break;
case 88: Item->augslot3unk = iInt; break;
case 89: Item->augslot4type = iInt; break;
case 90: Item->augslot4unk = iInt; break;
case 91: Item->augslot5type = iInt; break;
case 92: Item->augslot5unk = iInt; break;
case 93: Item->ldontheme = iInt; break;
case 94: Item->ldonprice = iInt; break;
case 95: Item->ldonsold = iInt; break;
case 96: Item->bagtype = iInt; break;
case 97: Item->bagslots = iInt; break;
case 98: Item->bagsize = iInt; break;
case 99: Item->bagwr = iInt; break;
case 100: Item->book = iInt; break;
case 101: Item->booktype = iInt; break;
case 102: Item->filename = iInt; break;
case 103: Item->banedmgraceamt = iInt; break;
case 104: Item->augrestrict = iInt; break;
case 105: Item->loregroup = iInt; break;
case 106: Item->pendingloreflag = iInt; break;
case 107: Item->artifactflag = iInt; break;
case 108: Item->summonedflag = iInt; break;
case 109: Item->favor = iInt; break;
case 110: Item->fvnodrop = iInt; break;
case 111: Item->endur = iInt; break;
case 112: Item->dotshielding = iInt; break;
case 113: Item->attack = iInt; break;
case 114: Item->regen = iInt; break;
case 115: Item->manaregen = iInt; break;
case 116: Item->endregen = iInt; break;
case 117: Item->haste = iInt; break;
case 118: Item->damageshield = iInt; break;
case 119: Item->recastdelay = iInt; break;
case 120: Item->recasttype = iInt; break;
case 121: Item->guildfavor = iInt; break;
case 122: Item->augdistiller = iInt; break;
case 123: Item->UNK123 = iInt; break;
case 124: Item->UNK124 = iInt; break;
case 125: Item->attuneable = iInt; break;
case 126: Item->nopet = iInt; break;
case 127: Item->UNK127 = iInt; break;
case 128: Item->pointtype = iInt; break;
case 129: Item->potionbelt = iInt; break;
case 130: Item->UNK130 = iInt; break;
case 131: Item->stacksize = iInt; break;
case 132: Item->notransfer = iInt; break;
case 133: Item->clickeffect = iInt; break;
case 134: Item->clicktype = iInt; break;
case 135: Item->clicklevel2 = iInt; break;
case 136: Item->clicklevel = iInt; break;
case 137: Item->UNK137 = iInt; break;
case 138: Item->proceffect = iInt; break;
case 139: Item->proctype = iInt; break;
case 140: Item->proclevel2 = iInt; break;
case 141: Item->proclevel = iInt; break;
case 142: Item->UNK142 = iInt; break;
case 143: Item->worneffect = iInt; break;
case 144: Item->worntype = iInt; break;
case 145: Item->wornlevel2 = iInt; break;
case 146: Item->wornlevel = iInt; break;
case 147: Item->UNK147 = iInt; break;
case 148: Item->focuseffect = iInt; break;
case 149: Item->focustype = iInt; break;
case 150: Item->focuslevel2 = iInt; break;
case 151: Item->focuslevel = iInt; break;
case 152: Item->UNK152 = iInt; break;
case 153: Item->scrolleffect = iInt; break;
case 154: Item->scrolltype = iInt; break;
case 155: Item->scrolllevel2 = iInt; break;
case 156: Item->scrolllevel = iInt; break;
case 157: Item->UNK157 = iInt; break;
case 158: Item->verified = iInt; break;
}
}
//
// void ReadItem (char * cLine)
//
static void ReadItem (PEQITEM Item, char * cLine)
{
char * cPtr = cLine;
int iField = 0;
while (*cPtr) {
char cField[256];
char * cDest = cField;
while (*cPtr != '|' && *cPtr != '\0') {
*(cDest++) = *(cPtr++);
}
*cDest = '\0';
cPtr++;
SetField (Item, iField++, cField);
}
}
typedef unsigned long uint32_t;
//
// uint32_t calc_hash (const char *string)
//
static uint32_t calc_hash (const char *string)
{
register hash = 0;
while (*string != '\0') {
register c = toupper(*string);
hash *= 0x1F;
hash += (int) c;
string++;
}
return hash;
}
//
// void MakeLink (PEQITEM Item)
//
static void MakeLink (PEQITEM Item, char * cLink)
{
char hashstr[512];
if(Item->itemclass == 0 && Item->charmfileid != 0) {
sprintf(hashstr, "%d%s-1-1-1-1-1%d %d %d %d %d %d %d %d %d",
Item->id, Item->name,
Item->light, Item->icon, Item->price, Item->size,
Item->weight, Item->itemclass, Item->itemtype, Item->favor,
Item->guildfavor);
//WriteChatf("Charm: %s", Item->name);
//WriteChatf("Hash: %s", hashstr);
} else if(Item->itemclass == 2) {
sprintf(hashstr, "%d%s%d%d%09X",
Item->id, Item->name,
Item->weight, Item->booktype, Item->price);
//WriteChatf("Book: %s", Item->name);
//WriteChatf("Hash: %s", hashstr);
} else if(Item->itemclass == 1) {
sprintf(hashstr, "%d%s%x%d%09X%d",
Item->id, Item->name,
Item->bagslots, Item->bagwr, Item->price, Item->weight);
//WriteChatf("Bag: %s", Item->name);
//WriteChatf("Hash: %s", hashstr);
} else {
sprintf(hashstr, "%d%s-1-1-1-1-1%d %d %d %d %d %d %d %d %d %d %d %d %d",
Item->id, Item->name,
Item->mana, Item->hp, Item->favor, Item->light,
Item->icon, Item->price, Item->weight, Item->reqlevel,
Item->size, Item->itemclass, Item->itemtype, Item->ac,
Item->guildfavor);
//WriteChatf("Item: %s", Item->name);
//WriteChatf("Hash: %s", hashstr);
}
uint32_t hash = calc_hash (hashstr);
if (Item->loregroup > 1000)
{
// Evolving
sprintf (cLink, "\x12%d%05X%05X%05X%05X%05X%05X%1d%04X%1X%08X%s\x12",
0,
Item->id,
0, 0, 0, 0, 0, // Augs
1, Item->loregroup, (Item->id%10)+1, // Evolving items (0=no 1=evolving, lore group, level)
hash, // Item hash
Item->name);
}
else
{
// Non-evolving
sprintf (cLink, "\x12%d%05X%05X%05X%05X%05X%05X%1d%04X%1X%08X%s\x12",
0,
Item->id,
0, 0, 0, 0, 0, // Augs
0, 0, 0, // Evolving items (0=no 1=evolving, lore group, level)
hash, // Item hash
Item->name);
}
}
//
// static int ConvertItemsDotTxt (void)
//
static int ConvertItemsDotTxt (void)
{
WriteChatf ("MQ2LinkDB: Importing items.txt...");
char cFilename[MAX_PATH];
sprintf(cFilename,"%s\\items.txt", gszINIPath);
FILE * File = fopen (cFilename, "rt");
if (File != NULL) {
char cFilename2[MAX_PATH];
sprintf(cFilename2, "%s\\MQ2LinkDB.txt", gszINIPath);
FILE * LinkFile = fopen (cFilename2, "wt");
if (LinkFile != NULL) {
if (abPresent != NULL) {
bKnowTotal = false;
free (abPresent);
abPresent = NULL;
}
WriteChatf ("MQ2LinkDB: Generating links...");
char cLine[1500];
bool bFirst = true;;
int iCount = 0;
while (fgets (cLine, 1500, File) != NULL) {
cLine[strlen (cLine) - 1] = '\0';
if (bFirst) {
// Small sanity check on file
char * cCheck = "itemclass|name|lore|idfile|id|weight|norent|nodrop|size|slots|price";
if (memcmp (cLine, cCheck, strlen (cCheck)) != 0) {
WriteChatf ("MQ2LinkDB: \arInvalid items.txt file. Aborting", iCount);
break;
}
bFirst = false;
} else {
EQITEM Item;
memset (&Item, 0, sizeof (Item));
ReadItem (&Item, cLine);
char cLink[256];
MakeLink (&Item, cLink);
//WriteChatf("Test ItemID: %d", ItemID(cLink));
fprintf (LinkFile, "%s\n", cLink);
iCount++;
}
}
WriteChatf ("MQ2LinkDB: Complete! \ay%d\ax links generated", iCount);
fclose (LinkFile);
} else {
WriteChatf ("MQ2LinkDB: \arCould not create link file (MQ2LinkDB.txt) (err: %d)", errno);
}
fclose (File);
} else {
WriteChatf ("MQ2LinkDB: \arSource file not found (items.txt)");
}
return 0;
}
Attachments
Last edited by a moderator: