474 lines
16 KiB
Plaintext
474 lines
16 KiB
Plaintext
/************************************************************************
|
|
* script name : socket_inc
|
|
* created by : eyesolated
|
|
* date : 2019/3/21
|
|
*
|
|
* description : Central include script for Socketing
|
|
*
|
|
* changes : 2019/3/21 - eyesolated - Initial creation
|
|
************************************************************************/
|
|
#include "socket_cfg"
|
|
#include "ip_inc"
|
|
#include "util_inc"
|
|
#include "nwnx_itemprop"
|
|
#include "prc_misc_const"
|
|
|
|
/*******************************************************************************
|
|
* FUNCTION DECLARATION
|
|
******************************************************************************/
|
|
|
|
// Creates the database tables used for Socketing
|
|
void socket_CreateTables();
|
|
|
|
// Returns FALSE if any of the required Socketing Tables is missing
|
|
int socket_GetTablesExist();
|
|
|
|
// Drops all Socketing Tables
|
|
void socket_DropTables();
|
|
|
|
// Adds information about a socketable item to the database
|
|
void socket_AddSocketableItem(string sResRef, int nItemCategory, int nItemProperty, int nItemSubProperty = -1);
|
|
|
|
// Returns the resref of a socketable item
|
|
string socket_GetSocketableItem();
|
|
|
|
// Retrieves socket information for sSocket
|
|
// Return Values: Socket does not exist - RETURN.* will be "" / 0 / OBJECT_INVALID depending on variable type
|
|
// Socket is empty - RETURN.Item_Socketed_ResRef will be SOCKET_SOCKETED_NONE
|
|
// Socketed - RETURN will hold all information
|
|
struct SOCKET_STRUCT_SOCKET_INFO socket_GetSocketInfo(object oItem, int nSocket = 1);
|
|
|
|
// Get the first empty socket of oItem
|
|
// Returns -1 if there is no empty socket available
|
|
int socket_GetEmptySocket(object oItem);
|
|
|
|
// Retrieves the itemproperty associated with oSocketableItem if socketed in oItem
|
|
itemproperty socket_GetProperty(object oItem, object oSocketableItem);
|
|
|
|
// Returns the Item Category (SOCKET_ITEMCATEGORY_*)
|
|
int socket_GetItemCategory(object oItem);
|
|
|
|
// (re)Sets the Description of oItem to include Socket information
|
|
void socket_SetDescription(object oItem);
|
|
|
|
// Inserts oItemToSocket into oItem and informs oPC about the result
|
|
// Returns TRUE on success, FALSE on failure
|
|
int socket_InsertSocketableItem(object oPC, object oItem, object oItemToSocket);
|
|
|
|
// Creates a new socket in the given position
|
|
// Returns FALSE if the socket couldn't be created, TRUE if it was created successfully
|
|
int socket_CreateSocket(object oItem, int nSocket = 1);
|
|
|
|
// Tries to create nCount Sockets in oItem (Maximum of 3).
|
|
// Returns the nubmer of new sockets created.
|
|
int socket_CreateSockets(object oItem, int nCount);
|
|
|
|
/*******************************************************************************
|
|
* FUNCTION IMPLEMENTATION
|
|
******************************************************************************/
|
|
|
|
int socket_GetTablesExist()
|
|
{
|
|
int nExists_Base = NWNX_SQL_ExecuteQuery("DESCRIBE " + SOCKET_TABLE_BASE);
|
|
|
|
return (nExists_Base);
|
|
}
|
|
|
|
void socket_DropTables()
|
|
{
|
|
NWNX_SQL_ExecuteQuery("DROP TABLE " + SOCKET_TABLE_BASE);
|
|
}
|
|
|
|
void socket_CreateTables()
|
|
{
|
|
// Base Table
|
|
string Table_Base = SOCKET_TABLE_BASE + " (" +
|
|
"ResRef varchar(16) NOT NULL," +
|
|
"ItemCategory int NOT NULL," +
|
|
"ItemProperty_ID int NOT NULL," +
|
|
"ItemSubProperty_ID int NOT NULL DEFAULT -1" +
|
|
")";
|
|
|
|
NWNX_SQL_ExecuteQuery("CREATE TABLE IF NOT EXISTS " + Table_Base);
|
|
}
|
|
|
|
void socket_AddSocketableItem(string sResRef, int nItemCategory, int nItemProperty, int nItemSubProperty = -1)
|
|
{
|
|
string sSQL = "INSERT INTO " + SOCKET_TABLE_BASE + " (ResRef, ItemCategory, ItemProperty_ID, ItemSubProperty_ID) ";
|
|
sSQL += "VALUES (?, ?, ?, ?)";
|
|
|
|
NWNX_SQL_PrepareQuery(sSQL);
|
|
NWNX_SQL_PreparedString(0, sResRef);
|
|
NWNX_SQL_PreparedInt(1, nItemCategory);
|
|
NWNX_SQL_PreparedInt(2, nItemProperty);
|
|
NWNX_SQL_PreparedInt(3, nItemSubProperty);
|
|
NWNX_SQL_ExecutePreparedQuery();
|
|
}
|
|
|
|
string socket_GetSocketableItem()
|
|
{
|
|
string sSQL = "SELECT DISCTINCT ResRef FROM " + SOCKET_TABLE_BASE + " ORDER BY RAND() LIMIT 1";
|
|
NWNX_SQL_ExecuteQuery(sSQL);
|
|
|
|
if (NWNX_SQL_ReadyToReadNextRow())
|
|
{
|
|
NWNX_SQL_ReadNextRow();
|
|
return NWNX_SQL_ReadDataInActiveRow(0);
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
struct SOCKET_STRUCT_SOCKET_INFO socket_GetSocketInfo(object oItem, int nSocket = 1)
|
|
{
|
|
struct SOCKET_STRUCT_SOCKET_INFO SocketInfo;
|
|
SocketInfo.Item_Socketed_ResRef = GetLocalString(oItem, SOCKET_VAR_SOCKET_RESREF + "_" + IntToString(nSocket));
|
|
|
|
// If there is no ResRef or is an empty socket, return right away
|
|
if (SocketInfo.Item_Socketed_ResRef == "" ||
|
|
SocketInfo.Item_Socketed_ResRef == SOCKET_SOCKETED_NONE)
|
|
return SocketInfo;
|
|
|
|
// Read the other variables
|
|
SocketInfo.Item_Socketed_Tag = GetLocalString(oItem, SOCKET_VAR_SOCKET_TAG + "_" + IntToString(nSocket));
|
|
SocketInfo.Item_Socketed_Name = GetLocalString(oItem, SOCKET_VAR_SOCKET_NAME + "_" + IntToString(nSocket));
|
|
|
|
return SocketInfo;
|
|
}
|
|
|
|
int socket_GetEmptySocket(object oItem)
|
|
{
|
|
int nSocket;
|
|
string ResRef;
|
|
for (nSocket = 1; nSocket <= SOCKET_MAXIMUM; nSocket++)
|
|
{
|
|
ResRef = GetLocalString(oItem, SOCKET_VAR_SOCKET_RESREF + "_" + IntToString(nSocket));
|
|
if (ResRef == "")
|
|
return -1;
|
|
else if (ResRef == SOCKET_SOCKETED_NONE)
|
|
return nSocket;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
itemproperty socket_GetProperty(object oItem, object oSocketableItem)
|
|
{
|
|
itemproperty Result;
|
|
int nItemCategory = socket_GetItemCategory(oItem);
|
|
if (nItemCategory == SOCKET_ITEMCATEGORY_UNKNOWN)
|
|
return Result;
|
|
|
|
string sItemToSocket_ResRef = GetResRef(oSocketableItem);
|
|
string sQuery;
|
|
sQuery = "SELECT A.ItemSubProperty_ID" +
|
|
", B." +CS_IP_ID +
|
|
", B." + CS_IP_VARONE +
|
|
", B." + CS_IP_VARTWO +
|
|
", B." + CS_IP_VARTHREE +
|
|
" FROM " + SOCKET_TABLE_BASE + " A" +
|
|
" JOIN " + CS_IP_TABLE + " B ON A.ItemProperty_ID = B." + CS_IP_ID +
|
|
" WHERE A.ResRef = ? AND (A.ItemCategory & ?) != 0 AND B." + CS_IP_LEVEL + " = ?";
|
|
NWNX_SQL_PrepareQuery(sQuery);
|
|
NWNX_SQL_PreparedString(0, sItemToSocket_ResRef);
|
|
NWNX_SQL_PreparedInt(1, nItemCategory);
|
|
NWNX_SQL_PreparedInt(2, GetLocalInt(oSocketableItem, SOCKET_VAR_ITEM_LEVEL));
|
|
NWNX_SQL_ExecutePreparedQuery();
|
|
|
|
if (NWNX_SQL_ReadyToReadNextRow())
|
|
{
|
|
NWNX_SQL_ReadNextRow();
|
|
int IP_SubProperty = StringToInt(NWNX_SQL_ReadDataInActiveRow(0));
|
|
int IP_ID = StringToInt(NWNX_SQL_ReadDataInActiveRow(1));
|
|
int IP_VarOne = StringToInt(NWNX_SQL_ReadDataInActiveRow(2));
|
|
int IP_VarTwo = StringToInt(NWNX_SQL_ReadDataInActiveRow(3));
|
|
int IP_VarThree = StringToInt(NWNX_SQL_ReadDataInActiveRow(4));
|
|
|
|
struct STRUCT_IP_PropertyDetails ip = ip_GetSpecificProperty(GetBaseItemType(oSocketableItem), IP_ID, IP_VarOne, IP_VarTwo, IP_VarThree);
|
|
Result = ip.IP;
|
|
|
|
// Force specific Property if there is one
|
|
if (IP_SubProperty != -1)
|
|
{
|
|
struct NWNX_IPUnpacked IPU = NWNX_ItemProperty_UnpackIP(Result);
|
|
IPU.nSubType = IP_SubProperty;
|
|
Result = NWNX_ItemProperty_PackIP(IPU);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WriteTimestampedLogEntry("SOCKET: Failed to retrieve DB values for [" + sItemToSocket_ResRef + "]");
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
int socket_GetItemCategory(object oItem)
|
|
{
|
|
int nBaseItemType = GetBaseItemType(oItem);
|
|
switch (nBaseItemType)
|
|
{
|
|
case BASE_ITEM_AMULET: return SOCKET_ITEMCATEGORY_JEWELRY_AMULET;
|
|
case BASE_ITEM_ARMOR: return SOCKET_ITEMCATEGORY_ARMOR;
|
|
case BASE_ITEM_BELT: return SOCKET_ITEMCATEGORY_BELT;
|
|
case BASE_ITEM_BOOTS: return SOCKET_ITEMCATEGORY_BOOTS;
|
|
case BASE_ITEM_BRACER:
|
|
case BASE_ITEM_BRACER_SHIELD: return SOCKET_ITEMCATEGORY_BRACERS;
|
|
case BASE_ITEM_CLOAK: return SOCKET_ITEMCATEGORY_CLOAK;
|
|
case BASE_ITEM_GLOVES: return SOCKET_ITEMCATEGORY_GLOVES;
|
|
case BASE_ITEM_HELMET: return SOCKET_ITEMCATEGORY_HELMET;
|
|
case BASE_ITEM_RING: return SOCKET_ITEMCATEGORY_JEWELRY_RING;
|
|
case BASE_ITEM_SMALLSHIELD:
|
|
case BASE_ITEM_LARGESHIELD:
|
|
case BASE_ITEM_TOWERSHIELD: return SOCKET_ITEMCATEGORY_SHIELD;
|
|
case BASE_ITEM_LIGHTCROSSBOW:
|
|
case BASE_ITEM_HEAVYCROSSBOW: return SOCKET_ITEMCATEGORY_RANGED_CROSSBOW;
|
|
case BASE_ITEM_SHORTBOW:
|
|
case BASE_ITEM_LONGBOW: return SOCKET_ITEMCATEGORY_RANGED_BOW;
|
|
case BASE_ITEM_SHORTSWORD:
|
|
case BASE_ITEM_LONGSWORD:
|
|
case BASE_ITEM_BATTLEAXE:
|
|
case BASE_ITEM_BASTARDSWORD:
|
|
case BASE_ITEM_LIGHTFLAIL:
|
|
case BASE_ITEM_WARHAMMER:
|
|
case BASE_ITEM_LIGHTMACE:
|
|
case BASE_ITEM_HALBERD:
|
|
case BASE_ITEM_TWOBLADEDSWORD:
|
|
case BASE_ITEM_GREATSWORD:
|
|
case BASE_ITEM_GREATAXE:
|
|
case BASE_ITEM_DAGGER:
|
|
case BASE_ITEM_CLUB:
|
|
case BASE_ITEM_DART:
|
|
case BASE_ITEM_DIREMACE:
|
|
case BASE_ITEM_DOUBLEAXE:
|
|
case BASE_ITEM_HEAVYFLAIL:
|
|
case BASE_ITEM_LIGHTHAMMER:
|
|
case BASE_ITEM_HANDAXE:
|
|
case BASE_ITEM_KAMA:
|
|
case BASE_ITEM_KATANA:
|
|
case BASE_ITEM_KUKRI:
|
|
case BASE_ITEM_MORNINGSTAR:
|
|
case BASE_ITEM_QUARTERSTAFF:
|
|
case BASE_ITEM_RAPIER:
|
|
case BASE_ITEM_SCYTHE:
|
|
case BASE_ITEM_SHORTSPEAR:
|
|
case BASE_ITEM_SHURIKEN:
|
|
case BASE_ITEM_SICKLE:
|
|
case BASE_ITEM_SCIMITAR:
|
|
case BASE_ITEM_SLING:
|
|
case BASE_ITEM_DWARVENWARAXE:
|
|
case BASE_ITEM_WHIP:
|
|
case BASE_ITEM_GRENADE:
|
|
case BASE_ITEM_TRIDENT:
|
|
case BASE_ITEM_THROWINGAXE:
|
|
case BASE_ITEM_CSLASHWEAPON:
|
|
case BASE_ITEM_CPIERCWEAPON:
|
|
case BASE_ITEM_CBLUDGWEAPON:
|
|
case BASE_ITEM_CSLSHPRCWEAP:
|
|
//:: PRC Weapons
|
|
case BASE_ITEM_LIGHT_LANCE:
|
|
case BASE_ITEM_HEAVY_PICK:
|
|
case BASE_ITEM_LIGHT_PICK:
|
|
case BASE_ITEM_SAI:
|
|
case BASE_ITEM_NUNCHAKU:
|
|
case BASE_ITEM_FALCHION:
|
|
case BASE_ITEM_SAP:
|
|
case BASE_ITEM_KATAR:
|
|
case BASE_ITEM_HEAVY_MACE:
|
|
case BASE_ITEM_MAUL:
|
|
case BASE_ITEM_DOUBLE_SCIMITAR:
|
|
case BASE_ITEM_GOAD:
|
|
case BASE_ITEM_EAGLE_CLAW:
|
|
case BASE_ITEM_ELVEN_LIGHTBLADE:
|
|
case BASE_ITEM_ELVEN_THINBLADE:
|
|
case BASE_ITEM_ELVEN_COURTBLADE:
|
|
// CEP weapons:
|
|
case 300: // trident_1h
|
|
case 301: // heavypick
|
|
case 302: // lightpick
|
|
case 303: // sai
|
|
case 304: // nunchaku
|
|
case 305: // falchion
|
|
case 308: // sap
|
|
case 309: // daggerassn
|
|
case 310: // katar
|
|
case 312: // lightmace2
|
|
case 313: // kukri2
|
|
case 316: // falchion_2
|
|
case 317: // heavy_mace
|
|
case 318: // maul
|
|
case 319: // mercurial_longsword
|
|
case 320: // mercurial_greatsword
|
|
case 321: // scimitar_double
|
|
case 322: // goad
|
|
case 323: // windfirewheel
|
|
case 324: // maugdoublesword
|
|
case 327: // Flowers_Crystal
|
|
case 329: // tool_2handed
|
|
case 330: // Longsword_2
|
|
return SOCKET_ITEMCATEGORY_MELEE;
|
|
}
|
|
|
|
return SOCKET_ITEMCATEGORY_UNKNOWN;
|
|
}
|
|
|
|
void socket_SetDescription(object oItem)
|
|
{
|
|
string sSaved = GetLocalString(oItem, SOCKET_VAR_ITEM_DESCRIPTION);
|
|
string sDescription = GetDescription(oItem);
|
|
string sSocketDescription = "\n";
|
|
int n;
|
|
struct SOCKET_STRUCT_SOCKET_INFO socketInfo;
|
|
for (n = 1; n <= SOCKET_MAXIMUM; n++)
|
|
{
|
|
socketInfo = socket_GetSocketInfo(oItem, n);
|
|
if (socketInfo.Item_Socketed_ResRef == "")
|
|
break;
|
|
else if (socketInfo.Item_Socketed_ResRef == SOCKET_SOCKETED_NONE)
|
|
sSocketDescription += "\n" + SOCKET_DESCRIPTION_SOCKET + SOCKET_DESCRIPTION_SOCKET_EMPTY;
|
|
else
|
|
sSocketDescription += "\n" + SOCKET_DESCRIPTION_SOCKET + GetLocalString(oItem, SOCKET_VAR_SOCKET_NAME + "_" + IntToString(n));
|
|
}
|
|
|
|
// If there is no socket description, return right away
|
|
if (sSocketDescription == "\n")
|
|
return;
|
|
|
|
// If saved is empty or the retrieved description shows no signs of socketing texts,
|
|
// save the "original/pre-Socketing" description and append sSocketDescription
|
|
if (sSaved == "" ||
|
|
FindSubString(sDescription, SOCKET_DESCRIPTION_SOCKET) == -1)
|
|
{
|
|
SetLocalString(oItem, SOCKET_VAR_ITEM_DESCRIPTION, sDescription);
|
|
sDescription += sSocketDescription;
|
|
}
|
|
// otherwise, use the saved "original/pre-Socketing" description and append there
|
|
else
|
|
sDescription = sSaved + sSocketDescription;
|
|
|
|
SetDescription(oItem, sDescription);
|
|
}
|
|
|
|
int socket_InsertSocketableItem(object oPC, object oItem, object oItemToSocket)
|
|
{
|
|
string sItemToSocket_ResRef = GetResRef(oItemToSocket);
|
|
|
|
// If this is the Socket Creator, create a socket
|
|
if (GetIsDM(oPC) &&
|
|
sItemToSocket_ResRef == "socket_creator")
|
|
{
|
|
socket_CreateSockets(oItem, 1);
|
|
return FALSE;
|
|
}
|
|
|
|
string sItemToSocket_Name = GetName(oItemToSocket);
|
|
|
|
// Find out if there's an empty socket
|
|
int nSocket = socket_GetEmptySocket(oItem);
|
|
|
|
if (nSocket == -1)
|
|
{
|
|
SendMessageToPC(oPC, "There is no empty socket available on " + GetName(oItem));
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
// Find out if this item is already socketed
|
|
int n = 1;
|
|
while (n < nSocket)
|
|
{
|
|
if (GetLocalString(oItem, SOCKET_VAR_SOCKET_RESREF + "_" + IntToString(n)) == sItemToSocket_ResRef)
|
|
{
|
|
SendMessageToPC(oPC, "A socket already contains " + sItemToSocket_Name);
|
|
return FALSE;
|
|
}
|
|
n++;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Retrieve information about the socketed item from Database
|
|
itemproperty IP = socket_GetProperty(oItem, oItemToSocket);
|
|
if (!GetIsItemPropertyValid(IP))
|
|
return FALSE;
|
|
|
|
// Add the identified item property to the item
|
|
IPSafeAddItemProperty(oItem, IP, 0.0f, X2_IP_ADDPROP_POLICY_KEEP_EXISTING);
|
|
|
|
// Set the local strings for the sockets on the item
|
|
SetLocalString(oItem, SOCKET_VAR_SOCKET_RESREF + "_" + IntToString(nSocket), sItemToSocket_ResRef);
|
|
SetLocalString(oItem, SOCKET_VAR_SOCKET_TAG + "_" + IntToString(nSocket), GetTag(oItemToSocket));
|
|
SetLocalString(oItem, SOCKET_VAR_SOCKET_NAME + "_" + IntToString(nSocket), sItemToSocket_Name);
|
|
|
|
// Destory the now socketed item
|
|
DestroyObject(oItemToSocket);
|
|
|
|
// Set the new description
|
|
socket_SetDescription(oItem);
|
|
|
|
// Tell the player he successfully socketed the item
|
|
SendMessageToPC(oPC, "You successfully socketed " + sItemToSocket_Name + " into " + GetName(oItem));
|
|
return TRUE;
|
|
}
|
|
|
|
int socket_CreateSocket(object oItem, int nSocket = 1)
|
|
{
|
|
// Only items of known Categories are able to get Sockets
|
|
if (socket_GetItemCategory(oItem) == SOCKET_ITEMCATEGORY_UNKNOWN)
|
|
return FALSE;
|
|
|
|
string sExisting = GetLocalString(oItem, SOCKET_VAR_SOCKET_RESREF + "_" + IntToString(nSocket));
|
|
|
|
// If the Socket already exists, return FALSE
|
|
if (sExisting != "")
|
|
return FALSE;
|
|
else
|
|
{
|
|
SetLocalString(oItem, SOCKET_VAR_SOCKET_RESREF + "_" + IntToString(nSocket), SOCKET_SOCKETED_NONE);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
int socket_CreateSockets(object oItem, int nCount)
|
|
{
|
|
// Only items of known Categories are able to get Sockets
|
|
if (socket_GetItemCategory(oItem) == SOCKET_ITEMCATEGORY_UNKNOWN)
|
|
return FALSE;
|
|
|
|
// Reduce nCount if needed
|
|
if (nCount > SOCKET_MAXIMUM)
|
|
nCount = SOCKET_MAXIMUM;
|
|
|
|
int nCreated = 0;
|
|
int n;
|
|
for (n = 1; n <= SOCKET_MAXIMUM; n++)
|
|
{
|
|
if (socket_CreateSocket(oItem, n))
|
|
nCreated++;
|
|
|
|
if (nCreated == nCount)
|
|
break;
|
|
}
|
|
|
|
if (nCreated > 0)
|
|
socket_SetDescription(oItem);
|
|
|
|
return nCreated;
|
|
}
|
|
|
|
void socket_CreateSocketableItem(object oTarget, int nLevel = -1)
|
|
{
|
|
// Select an item
|
|
string sResRef = socket_GetSocketableItem();
|
|
|
|
// Create selected item in oTarget's inventory
|
|
object oItem = CreateItemOnObject(sResRef, oTarget);
|
|
|
|
// Set Name of item (recoloring based on level/rarity
|
|
|
|
|
|
// Set level on item
|
|
SetLocalInt(oItem, SOCKET_VAR_ITEM_LEVEL, nLevel);
|
|
}
|