#include "adv_include" #include "nwnx_events" #include "inc_helper_funcs" // Item combination script // // This is fired when a player uses the "Combine" spell on an item to target another item in their inventory. // When another object is targeted, a combination will be looked up in the database to see if there's a match. // If a match is found, it will destroy both the used item and the target item and create the new item in the player's inventory. // For example: // Player combines a Green Herb with a Red Herb. Both of those herbs are destroyed and a new item named "Mixed Herb" is created in their inventory. // // If no match is found, the combination will fail and nothing will happen. // // Created by Zunath on July 19, 2011 ///////////////////// // SYSTEM MESSAGES // ///////////////////// // The message that displays when a combination fails. const string COMBINE_MESSAGE_FAILED = "Combination failed."; // The message that displays when a player has too low skill const string COMBINE_MESSAGE_LOW_SKILL = "Your mixing skill is too low to combine those items."; ////////////////////// // SYSTEM CONSTANTS // ////////////////////// // Name of the MySQL table which holds all valid combinations const string COMBINE_MYSQL_TABLE = "combine_chart"; /////////////////////// // SYSTEM PROTOTYPES // /////////////////////// // Handles creation of new item and destruction of the two items used to create it. // oPC = The player creating the item // oItemA = Object #1 // oItemB = Object #2 // sOutput = The resref of the item to be created // iQuantity = The number of items to create // sHQOutput = The resref of the "high quality" item // iHQQuantity = The number of "high quality" items to create // iMixingSkill = oPC's mixing skill level. This is used to determine if HQ items can be made // bItemAInfinite = If TRUE, item A will not be destroyed during combination // bItemBInfinite = If TRUE, item B will not be destroyed during combination // // SPECIAL CODES: // Quantity: // SA: Quantity and HQ quantity become the size of item A (listed in the database) // SB: Quantity and HQ quantity become the size of item B (listed in the database) void COMBINE_CombineItemProcess(object oPC, object oItemA, object oItemB, string sOutput, string sQuantity, string sHQOutput = "", string sHQQuantity = "", int iMixingSkill = 0, string sDatabaseItemA = "", string sDatabaseItemB = "", int bItemAInfinite = FALSE, int bItemBInfinite = FALSE); ////////////////////// // SYSTEM FUNCTIONS // ////////////////////// void COMBINE_CombineItemProcess(object oPC, object oItemA, object oItemB, string sOutput, string sQuantity, string sHQOutput = "", string sHQQuantity = "", int iMixingSkill = 0, string sDatabaseItemA = "", string sDatabaseItemB = "", int bItemAInfinite = FALSE, int bItemBInfinite = FALSE) { int iQuantity; int iHQQuantity = StringToInt(sHQQuantity); string sResrefA = GetResRef(oItemA); string sResrefB = GetResRef(oItemB); // Special quantity codes functionality: // SA: Quantity is the stack size of ItemA // SB: Quantity is the stack size of ItemB if(sQuantity == "SA") { if(sResrefB == sDatabaseItemA) iQuantity = GetItemStackSize(oItemB); if(sResrefA == sDatabaseItemA) iQuantity = GetItemStackSize(oItemA); iHQQuantity = iQuantity; } else if(sQuantity == "SB") { if(sResrefB == sDatabaseItemB) iQuantity = GetItemStackSize(oItemB); if(sResrefA == sDatabaseItemB) iQuantity = GetItemStackSize(oItemA); iHQQuantity = iQuantity; } // Otherwise use the specified quantities and HQ quantities else iQuantity = StringToInt(sQuantity); // Safety check if(iQuantity < 1) iQuantity = 1; if(!bItemAInfinite) { if(sResrefB == sDatabaseItemA) DestroyObject(oItemB); if(sResrefA == sDatabaseItemA) DestroyObject(oItemA); } if(!bItemBInfinite) { if(sResrefB == sDatabaseItemB) DestroyObject(oItemB); if(sResrefA == sDatabaseItemB) DestroyObject(oItemA); } string sOutputResref = sOutput; int bHQMade = FALSE; // Check to see if this is an HQ creation if(sHQOutput != "" && iHQQuantity > 0 && iMixingSkill > 0) { // 50% chance to HQ at max mixing level (Level 20) int iChance = FloatToInt(iMixingSkill * 2.5); if(d100(1) <= iChance) { sOutputResref = sHQOutput; iQuantity = iHQQuantity; bHQMade = TRUE; } } // Mixing skill also allows players to make more of an item than normal, if they aren't making an HQ item already // The quantity made increases at a rate of an additional 2.5% per level of mixing. if(!bHQMade && iQuantity > 1) { iQuantity = FloatToInt(((1 + (0.025 * iMixingSkill)) * iQuantity)); } object oOutput = CreateItemOnObjectSafe(sOutputResref, oPC, iQuantity); int iStackSize = GetItemStackSize(oOutput); // Some items aren't stackable. If that's the case then we need to loop through and create each item if(iStackSize == 1) { iQuantity --; int iCurrentItem; for(iCurrentItem = 1; iCurrentItem <= iQuantity; iCurrentItem++) { CreateItemOnObjectSafe(sOutputResref, oPC, iQuantity); } } // Sometimes the item's stack size will be reached. If that's the case then we need to continue spawning // items until all of them have been created else if(iStackSize < iQuantity) { // We've already created the first stack, so remove that from the quantity iQuantity = iQuantity - iStackSize; while(iQuantity > 0) { // Still at least as large as the max stack size - create the new stack and reduce quantity if(iQuantity >= iStackSize) { CreateItemOnObjectSafe(sOutputResref, oPC, iStackSize); iQuantity = iQuantity - iStackSize; } // There's no more to create after this stack - create the new stack and set quantity to 0 else if(iQuantity < iStackSize) { CreateItemOnObjectSafe(sOutputResref, oPC, iQuantity); iQuantity = 0; } } } } void main() { object oPC = OBJECT_SELF; object oItemA; object oItemB; if(ADV_USING_LINUX) { oItemA = GetEventItem(); oItemB = GetEventTarget(); } else { oItemA = GetSpellTargetObject(); oItemB = GetSpellCastItem(); } string sItemAResref = GetResRef(oItemA); string sItemBResref = GetResRef(oItemB); // Player's skill in mixing int iMixingSkill = ADV_GetUpgradeLevel(oPC, ADV_ID_MIXING); // You can't combine items that aren't in your inventory. // You also can't if both of the items are the exact same. if(GetItemPossessor(oItemA) != oPC || GetItemPossessor(oItemB) != oPC || oItemA == oItemB) { SendMessageToPC(oPC, ColorTokenRed() + COMBINE_MESSAGE_FAILED + ColorTokenEnd()); return; } string sSQL = "SELECT ItemA, ItemB, ItemAInfinite, ItemBInfinite, Output, Quantity, HQOutput, HQQuantity, MixingRequired FROM " + COMBINE_MYSQL_TABLE + " WHERE ItemA='" + sItemAResref + "' AND ItemB='" + sItemBResref +"'"; SQLExecDirect(sSQL); // Combination is valid - create the new item and destroy the old ones. if(SQLFetch() == SQL_SUCCESS) { string sDatabaseItemA = SQLGetData(1); string sDatabaseItemB = SQLGetData(2); int bItemAInfinite = StringToInt(SQLGetData(3)); int bItemBInfinite = StringToInt(SQLGetData(4)); string sOutput = SQLGetData(5); string sQuantity = SQLGetData(6); string sHQOutput = SQLGetData(7); string sHQQuantity = SQLGetData(8); int iMixingRequired = StringToInt(SQLGetData(9)); if(iMixingSkill >= iMixingRequired) { COMBINE_CombineItemProcess(oPC, oItemA, oItemB, sOutput, sQuantity, sHQOutput, sHQQuantity, iMixingSkill, sDatabaseItemA, sDatabaseItemB, bItemAInfinite, bItemBInfinite); } else { SendMessageToPC(oPC, ColorTokenRed() + COMBINE_MESSAGE_LOW_SKILL + " (Required: " + IntToString(iMixingRequired) + ")" + ColorTokenEnd()); } return; } // Combination not valid - Try the alternative ordering. Note that IgnoreOrder field must be TRUE in order for this to work. else { sSQL = "SELECT ItemA, ItemB, ItemAInfinite, ItemBInfinite, Output, Quantity, HQOutput, HQQuantity, MixingRequired, IgnoreOrder FROM " + COMBINE_MYSQL_TABLE + " WHERE ItemB='" + sItemAResref + "' AND ItemA='" + sItemBResref +"'"; SQLExecDirect(sSQL); if(SQLFetch() == SQL_SUCCESS) { string sDatabaseItemA = SQLGetData(1); string sDatabaseItemB = SQLGetData(2); int bItemAInfinite = StringToInt(SQLGetData(3)); int bItemBInfinite = StringToInt(SQLGetData(4)); string sOutput = SQLGetData(5); string sQuantity = SQLGetData(6); string sHQOutput = SQLGetData(7); string sHQQuantity = SQLGetData(8); int iMixingRequired = StringToInt(SQLGetData(9)); int bIgnoreOrder = StringToInt(SQLGetData(10)); // IgnoreOrder field must be true in order for this to work. if(bIgnoreOrder) { if(iMixingSkill >= iMixingRequired) { COMBINE_CombineItemProcess(oPC, oItemA, oItemB, sOutput, sQuantity, sHQOutput, sHQQuantity, iMixingSkill, sDatabaseItemA, sDatabaseItemB, bItemAInfinite, bItemBInfinite); } else { SendMessageToPC(oPC, ColorTokenRed() + COMBINE_MESSAGE_LOW_SKILL + ColorTokenEnd()); } return; } } } // If we reach this point, then the combination has failed. SendMessageToPC(oPC, ColorTokenRed() + COMBINE_MESSAGE_FAILED + ColorTokenEnd()); }