Files
WindTemple/src/include/prc_craft_cv_inc.nss
Jaysyn904 6899d53372 2026/03/13 Initial Upload
Initial Upload
2026-03-13 12:51:11 -04:00

1057 lines
37 KiB
Plaintext

//:: prc_craft_cv_inc.nss
#include "prc_craft_inc"
#include "inc_dynconv"
//////////////////////////////////////////////////
/* Constant defintions */
//////////////////////////////////////////////////
//const int STAGE_ = ;
const int STAGE_START = 0;
const int STAGE_SELECT_SUBTYPE = 1;
const int STAGE_SELECT_COSTTABLEVALUE = 2;
const int STAGE_SELECT_PARAM1VALUE = 3;
const int STAGE_CONFIRM = 4;
const int STAGE_BANE = 5;
const int STAGE_CONFIRM_MAGIC = 7;
const int STAGE_APPEARANCE = 8;
const int STAGE_CLONE = 9;
const int STAGE_APPEARANCE_LIST = 10;
const int STAGE_APPEARANCE_VALUE = 11;
const int STAGE_CRAFT_GOLEM = 12;
const int STAGE_CRAFT_GOLEM_HD = 13;
const int STAGE_CRAFT_ALCHEMY = 14;
const int STAGE_CONFIRM_ALCHEMY = 15;
const int STAGE_CRAFT_POISON = 16;
const int STAGE_CONFIRM_POISON = 17;
const int STAGE_CRAFT_LICH = 18;
const int STAGE_CRAFT = 101;
const int STAGE_CRAFT_SELECT = 102;
const int STAGE_CRAFT_MASTERWORK = 103;
const int STAGE_CRAFT_AC = 104;
const int STAGE_CRAFT_MIGHTY = 105;
const int STAGE_CRAFT_CONFIRM = 106;
//const int STAGE_CRAFT = 101;
//const int CHOICE_ = ;
//these must be past the highest 2da entry to be read
const int CHOICE_FORGE = 20001;
const int CHOICE_BOOST = 20002;
const int CHOICE_BACK = 20003;
const int CHOICE_CLEAR = 20004;
const int CHOICE_CONFIRM = 20005;
const int CHOICE_SETNAME = 20006;
const int CHOICE_SETAPPEARANCE = 20007;
const int CHOICE_CLONE = 20008;
const int CHOICE_APPEARANCE_SHOUT = 20009;
const int CHOICE_APPEARANCE_SELECT = 20010;
const int CHOICE_PLUS_1 = 20011;
const int CHOICE_PLUS_10 = 20012;
const int CHOICE_MINUS_1 = 20013;
const int CHOICE_MINUS_10 = 20014;
const int CHOICE_CRAFT = 20101;
//const int NUM_MAX_COSTTABLEVALUES = 70;
//const int NUM_MAX_PARAM1VALUES = 70;
const int HAS_SUBTYPE = 1;
const int HAS_COSTTABLE = 2;
const int HAS_PARAM1 = 4;
const int STRREF_YES = 4752; // "Yes"
const int STRREF_NO = 4753; // "No"
const string PRC_CRAFT_ITEM = "PRC_CRAFT_ITEM";
const string PRC_CRAFT_TYPE = "PRC_CRAFT_TYPE";
const string PRC_CRAFT_SUBTYPE = "PRC_CRAFT_SUBTYPE";
const string PRC_CRAFT_SUBTYPEVALUE = "PRC_CRAFT_SUBTYPEVALUE";
const string PRC_CRAFT_COSTTABLE = "PRC_CRAFT_COSTTABLE";
const string PRC_CRAFT_COSTTABLEVALUE = "PRC_CRAFT_COSTTABLEVALUE";
const string PRC_CRAFT_PARAM1 = "PRC_CRAFT_PARAM1";
const string PRC_CRAFT_PARAM1VALUE = "PRC_CRAFT_PARAM1VALUE";
const string PRC_CRAFT_PROPLIST = "PRC_CRAFT_PROPLIST";
const string PRC_CRAFT_COST = "PRC_CRAFT_COST";
const string PRC_CRAFT_XP = "PRC_CRAFT_XP";
const string PRC_CRAFT_TIME = "PRC_CRAFT_TIME";
//const string PRC_CRAFT_BLUEPRINT = "PRC_CRAFT_BLUEPRINT";
const string PRC_CRAFT_CONVO_ = "PRC_CRAFT_CONVO_";
const string PRC_CRAFT_BASEITEMTYPE = "PRC_CRAFT_BASEITEMTYPE";
const string PRC_CRAFT_AC = "PRC_CRAFT_AC";
const string PRC_CRAFT_MIGHTY = "PRC_CRAFT_MIGHTY";
const string PRC_CRAFT_MATERIAL = "PRC_CRAFT_MATERIAL";
const string PRC_CRAFT_TAG = "PRC_CRAFT_TAG";
const string PRC_CRAFT_LINE = "PRC_CRAFT_LINE";
const string PRC_CRAFT_FILE = "PRC_CRAFT_FILE";
const string PRC_CRAFT_MAGIC_ENHANCE = "PRC_CRAFT_MAGIC_ENHANCE";
const string PRC_CRAFT_MAGIC_ADDITIONAL = "PRC_CRAFT_MAGIC_ADDITIONAL";
const string PRC_CRAFT_MAGIC_EPIC = "PRC_CRAFT_MAGIC_EPIC";
const string PRC_CRAFT_SCRIPT_STATE = "PRC_CRAFT_SCRIPT_STATE";
const string ARTIFICER_PREREQ_RACE = "ARTIFICER_PREREQ_RACE";
const string ARTIFICER_PREREQ_ALIGN = "ARTIFICER_PREREQ_ALIGN";
const string ARTIFICER_PREREQ_CLASS = "ARTIFICER_PREREQ_CLASS";
const string ARTIFICER_PREREQ_SPELL1 = "ARTIFICER_PREREQ_SPELL1";
const string ARTIFICER_PREREQ_SPELL2 = "ARTIFICER_PREREQ_SPELL2";
const string ARTIFICER_PREREQ_SPELL3 = "ARTIFICER_PREREQ_SPELL3";
const string ARTIFICER_PREREQ_SPELLOR1 = "ARTIFICER_PREREQ_SPELLOR1";
const string ARTIFICER_PREREQ_SPELLOR2 = "ARTIFICER_PREREQ_SPELLOR2";
const string ARTIFICER_PREREQ_COMPLETE = "ARTIFICER_PREREQ_COMPLETE";
const int PRC_CRAFT_STATE_NORMAL = 1;
const int PRC_CRAFT_STATE_MAGIC = 2;
const string PRC_CRAFT_HB = "PRC_CRAFT_HB";
const int SORT = TRUE; // If the sorting takes too much CPU, set to FALSE
const int DEBUG_LIST = FALSE;
//////////////////////////////////////////////////
/* Function defintions */
//////////////////////////////////////////////////
void PrintList(object oPC)
{
string tp = "Printing list:\n";
string s = GetLocalString(oPC, "ForgeConvo_List_Head");
if(s == ""){
tp += "Empty\n";
}
else{
tp += s + "\n";
s = GetLocalString(oPC, "ForgeConvo_List_Next_" + s);
while(s != ""){
tp += "=> " + s + "\n";
s = GetLocalString(oPC, "ForgeConvo_List_Next_" + s);
}
}
DoDebug(tp);
}
/**
* Creates a linked list of entries that is sorted into alphabetical order
* as it is built.
* Assumption: Power names are unique.
*
* @param oPC The storage object aka whomever is gaining powers in this conversation
* @param sChoice The choice string
* @param nChoice The choice value
*/
void AddToTempList(object oPC, string sChoice, int nChoice)
{
if(DEBUG_LIST) DoDebug("\nAdding to temp list: '" + sChoice + "' - " + IntToString(nChoice));
if(DEBUG_LIST) PrintList(oPC);
// If there is nothing yet
if(!GetLocalInt(oPC, "PRC_CRAFT_CONVO_ListInited"))
{
SetLocalString(oPC, "PRC_CRAFT_CONVO_List_Head", sChoice);
SetLocalInt(oPC, "PRC_CRAFT_CONVO_List_" + sChoice, nChoice);
SetLocalInt(oPC, "PRC_CRAFT_CONVO_ListInited", TRUE);
}
else
{
// Find the location to instert into
string sPrev = "", sNext = GetLocalString(oPC, "PRC_CRAFT_CONVO_List_Head");
while(sNext != "" && StringCompare(sChoice, sNext) >= 0)
{
if(DEBUG_LIST) DoDebug("Comparison between '" + sChoice + "' and '" + sNext + "' = " + IntToString(StringCompare(sChoice, sNext)));
sPrev = sNext;
sNext = GetLocalString(oPC, "PRC_CRAFT_CONVO_List_Next_" + sNext);
}
// Insert the new entry
// Does it replace the head?
if(sPrev == "")
{
if(DEBUG_LIST) DoDebug("New head");
SetLocalString(oPC, "PRC_CRAFT_CONVO_List_Head", sChoice);
}
else
{
if(DEBUG_LIST) DoDebug("Inserting into position between '" + sPrev + "' and '" + sNext + "'");
SetLocalString(oPC, "PRC_CRAFT_CONVO_List_Next_" + sPrev, sChoice);
}
SetLocalString(oPC, "PRC_CRAFT_CONVO_List_Next_" + sChoice, sNext);
SetLocalInt(oPC, "PRC_CRAFT_CONVO_List_" + sChoice, nChoice);
}
}
/**
* Reads the linked list built with AddToTempList() to AddChoice() and
* deletes it.
*
* @param oPC A PC gaining powers at the moment
*/
void TransferTempList(object oPC)
{
string sChoice = GetLocalString(oPC, "PRC_CRAFT_CONVO_List_Head");
int nChoice = GetLocalInt (oPC, "PRC_CRAFT_CONVO_List_" + sChoice);
DeleteLocalString(oPC, "PRC_CRAFT_CONVO_List_Head");
string sPrev;
if(DEBUG_LIST) DoDebug("Head is: '" + sChoice + "' - " + IntToString(nChoice));
while(sChoice != "")
{
// Add the choice
AddChoice(sChoice, nChoice, oPC);
// Get next
sChoice = GetLocalString(oPC, "PRC_CRAFT_CONVO_List_Next_" + (sPrev = sChoice));
nChoice = GetLocalInt (oPC, "PRC_CRAFT_CONVO_List_" + sChoice);
if(DEBUG_LIST) DoDebug("Next is: '" + sChoice + "' - " + IntToString(nChoice) + "; previous = '" + sPrev + "'");
// Delete the already handled data
DeleteLocalString(oPC, "PRC_CRAFT_CONVO_List_Next_" + sPrev);
DeleteLocalInt (oPC, "PRC_CRAFT_CONVO_List_" + sPrev);
}
DeleteLocalInt(oPC, "PRC_CRAFT_CONVO_ListInited");
}
//Returns the next conversation stage according
// to item property
int GetNextItemPropStage(int nStage, object oPC, int nPropList)
{
nStage++;
if(nStage == STAGE_SELECT_SUBTYPE && !(nPropList & HAS_SUBTYPE))
nStage++;
if(nStage == STAGE_SELECT_COSTTABLEVALUE && !(nPropList & HAS_COSTTABLE))
nStage++;
if(nStage == STAGE_SELECT_PARAM1VALUE && !(nPropList & HAS_PARAM1))
nStage++;
MarkStageNotSetUp(nStage, oPC);
return nStage;
}
//Returns the previous conversation stage according
// to item property
int GetPrevItemPropStage(int nStage, object oPC, int nPropList)
{
nStage--;
if(nStage == STAGE_SELECT_PARAM1VALUE && !(nPropList & HAS_PARAM1))
nStage--;
if(nStage == STAGE_SELECT_COSTTABLEVALUE && !(nPropList & HAS_COSTTABLE))
nStage--;
if(nStage == STAGE_SELECT_SUBTYPE && !(nPropList & HAS_SUBTYPE))
nStage--;
MarkStageNotSetUp(nStage, oPC);
return nStage;
}
//hardcoded to save time/prevent tmi
int SkipLineSpells(int i)
{
switch(i)
{
//i +2
case 3:
case 6:
case 16:
case 22:
case 26:
case 29:
case 38:
case 41:
case 44:
case 58:
case 61:
case 64:
case 70:
case 73:
case 79:
case 82:
case 96:
case 111:
case 116:
case 134:
case 145:
case 165:
case 185:
case 193:
case 202:
case 289:
case 292:
case 295:
case 312:
case 516:
case 1017:
case 1038:
case 1041:
case 1068:
case 1082:
case 1090:
case 1099:
case 1104:
case 1107:
case 1134:
case 1363:
case 1430:
case 1435: i = i + 2; break;
//i +1
case 10:
case 12:
case 19:
case 21:
case 24:
case 32:
case 34:
case 36:
case 47:
case 51:
case 53:
case 56:
case 67:
case 85:
case 91:
case 93:
case 102:
case 107:
case 109:
case 114:
case 124:
case 132:
case 138:
case 141:
case 156:
case 163:
case 181:
case 191:
case 196:
case 199:
case 214:
case 218:
case 220:
case 222:
case 224:
case 235:
case 237:
case 248:
case 252:
case 256:
case 258:
case 263:
case 276:
case 285:
case 306:
case 309:
case 315:
case 325:
case 397:
case 462:
case 475:
case 485:
case 514:
case 949:
case 953:
case 1001:
case 1003:
case 1005:
case 1007:
case 1009:
case 1011:
case 1013:
case 1020:
case 1031:
case 1034:
case 1043:
case 1045:
case 1048:
case 1050:
case 1052:
case 1055:
case 1057:
case 1059:
case 1061:
case 1063:
case 1076:
case 1078:
case 1086:
case 1088:
case 1095:
case 1097:
case 1102:
case 1111:
case 1113:
case 1119:
case 1121:
case 1124:
case 1126:
case 1128:
case 1145:
case 1147:
case 1196:
case 1205:
case 1215:
case 1260:
case 1350: i = i + 1; break;
case 173: i = 179; break;
case 317: i = 321; break;
case 328: i = 345; break;
case 359: i = 360; break;
case 400: i = 450; break;
case 487: i = 514; break;
case 520: i = 538; break;
case 540: i = 899; break;
case 902: i = 903; break;
case 914: i = 928; break;
case 967: i = 1000; break;
case 1366: i = 1369; break;
case 1389: i = 1416; break;
}
return i;
}
//added by MSB - hardcoded to prevent TMI
int SkipLineFeats(int i)
{
switch (i)
{
case 40: i = 99; break;
case 141: i = 201; break;
case 213: i = 257; break;
case 259: i = 260; break;
case 262: i = 264; break;
case 266: i = 343; break;
case 381: i = 394; break;
case 395: i = 24813; break;
}
return i;
}
//hardcoded to save time/prevent tmi
int SkipLineItemprops(int i)
{
switch(i)
{
case 94: i = 100; break;
case 102: i = 133; break;
case 135: i = 150; break;
case 151: i = 200; break;
}
return i;
}
//Adds names to a list based on sTable (2da), delayed recursion
// to avoid TMI
void PopulateList(object oPC, int MaxValue, int bSort, string sTable, object oItem = OBJECT_INVALID, int i = 0)
{
if(GetLocalInt(oPC, "DynConv_Waiting") == FALSE)
return;
if(i <= MaxValue)
{
int bValid = TRUE;
string sTemp = "";
if(sTable == "iprp_spells")
{
i = SkipLineSpells(i);
MaxValue = 1150; //MSB changed this from 540
}
else if(sTable == "IPRP_FEATS")
{
MaxValue = 24819;
i = SkipLineFeats(i);
}
else if(sTable == "itempropdef")
{
i = SkipLineItemprops(i);
bValid = ValidProperty(oItem, i);
if(bValid)
bValid = !GetPRCSwitch("PRC_CRAFT_DISABLE_itempropdef_" + IntToString(i));
}
else if(GetStringLeft(sTable, 6) == "craft_")
bValid = array_get_int(oPC, PRC_CRAFT_ITEMPROP_ARRAY, i);
sTemp = Get2DACache(sTable, "Name", i);
if((sTemp != "") && bValid)//this is going to kill
{
if(sTable == "iprp_spells")
{
AddToTempList(oPC, ActionString(GetStringByStrRef(StringToInt(sTemp))), i);
}
else
{
if(bSort)
AddToTempList(oPC, ActionString(GetStringByStrRef(StringToInt(sTemp))), i);
else
AddChoice(ActionString(GetStringByStrRef(StringToInt(sTemp))), i, oPC);
}
}
if(!(i % 100) && i) //i != 0, i % 100 == 0
//following line is for debugging
//FloatingTextStringOnCreature(sTable, oPC, FALSE);
FloatingTextStringOnCreature("*Tick*", oPC, FALSE);
}
else
{
if(bSort) TransferTempList(oPC);
DeleteLocalInt(oPC, "DynConv_Waiting");
FloatingTextStringOnCreature("*Done*", oPC, FALSE);
return;
}
DelayCommand(0.01, PopulateList(oPC, MaxValue, bSort, sTable, oItem, i + 1));
}
//use heartbeat
void ApplyProperties(object oPC, object oItem, itemproperty ip, int nCost, int nXP, string sFile, int nLine)
{
if(DEBUG) DoDebug("ApplyProperties: Starting with nCost=" + IntToString(nCost) +
", and Crafter GP =" + IntToString(GetGold(oPC)));
if(GetGold(oPC) < nCost)
{
FloatingTextStringOnCreature("Crafting: Insufficient gold!", oPC);
return;
}
int nHD = GetHitDice(oPC);
int nMinXP = nHD * (nHD - 1) * 500;
int nCurrentXP = GetXP(oPC);
if((nCurrentXP - nMinXP) < nXP)
{
FloatingTextStringOnCreature("Crafting: Insufficient XP!", oPC);
return;
}
if(GetItemPossessor(oItem) != oPC)
{
FloatingTextStringOnCreature("Crafting: You do not have the item!", oPC);
return;
}
if(DEBUG) DoDebug("ApplyProperties: Passed validation, about to apply properties");
if(nLine == -1)
IPSafeAddItemProperty(oItem, ip, 0.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING);
else if(nLine == -2)
{ //clone item
CopyItem(oItem, oPC, TRUE);
}
else
{
string sPropertyType = Get2DACache(sFile, "PropertyType", nLine);
if(sPropertyType == "M")
{ //checking required spells
if(!CheckCraftingSpells(oPC, sFile, nLine, TRUE))
{
FloatingTextStringOnCreature("Crafting: Required spells not available!", oPC);
return;
}
}
else if(sPropertyType == "P")
{
if(!CheckCraftingPowerPoints(oPC, sFile, nLine, TRUE))
{
FloatingTextStringOnCreature("Crafting: Insufficient power points!", oPC);
return;
}
}
if(DEBUG) DoDebug("ApplyProperties: Calling ApplyItemProps()");
ApplyItemProps(oItem, sFile, nLine);
}
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_BREACH), oPC);
// With this conditional logic:
if(GetLocalInt(oPC, "PRC_CRAFT_RESTORED"))
{
AssignCommand(oPC, ClearAllActions());
if(DEBUG) DoDebug("ApplyProperties: About to call TakeGoldFromCreature() with cost=" + IntToString(nCost));
AssignCommand(oPC, TakeGoldFromCreature(nCost, oPC, TRUE));
SpendXP(oPC, nXP);
DeleteLocalInt(oPC, "PRC_CRAFT_RESTORED");
}
else
{
AssignCommand(oPC, ClearAllActions());
if(DEBUG) DoDebug("ApplyProperties: About to call TakeGoldFromCreature() with cost=" + IntToString(nCost));
AssignCommand(oPC, TakeGoldFromCreature(nCost, oPC, TRUE));
if(DEBUG) DoDebug("ApplyProperties: TakeGoldFromCreature() completed, new gold =" + IntToString(GetGold(oPC)));
SpendXP(oPC, nXP);
if(DEBUG) DoDebug("ApplyProperties: XP deduction completed, new XP=" + IntToString(GetXP(oPC)));
}
//if(DEBUG) DoDebug("ApplyProperties: About to deduct gold ("+IntToString(nCost)+") and XP ("+IntToString(nCost)+").");
//TakeGoldFromCreature(nCost, oPC, TRUE);
//SetXP(oPC, GetXP(oPC) - nXP);
if(DEBUG) DoDebug("ApplyProperties: Completed successfully");
}
//use heartbeat
void CreateGolem(object oPC, int nCost, int nXP, string sFile, int nLine)
{
if(GetGold(oPC) < nCost)
{
FloatingTextStringOnCreature("Crafting: Insufficient gold!", oPC);
return;
}
int nHD = GetHitDice(oPC);
int nMinXP = nHD * (nHD - 1) * 500;
int nCurrentXP = GetXP(oPC);
if((nCurrentXP - nMinXP) < nXP)
{
FloatingTextStringOnCreature("Crafting: Insufficient XP!", oPC);
return;
}
string sPropertyType = Get2DACache(sFile, "CasterType", nLine);
if(sPropertyType == "M")
{ //checking required spells
if(!CheckCraftingSpells(oPC, sFile, nLine, TRUE))
{
FloatingTextStringOnCreature("Crafting: Required spells not available!", oPC);
return;
}
}
else if(sPropertyType == "P")
{
if(!CheckCraftingPowerPoints(oPC, sFile, nLine, TRUE))
{
FloatingTextStringOnCreature("Crafting: Insufficient power points!", oPC);
return;
}
}
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_BREACH), oPC);
TakeGoldFromCreature(nCost, oPC, TRUE);
SetXP(oPC, GetXP(oPC) - nXP);
}
int ArtificerPrereqCheck(object oPC, string sFile, int nLine, int nCost)
{
string sTemp, sSub, sSpell;
int nRace, nAlignGE, nAlignLC, nClass, i, j, bBreak, nLength, nPosition, nTemp;
int nSpell1, nSpell2, nSpell3, nSpellOR1, nSpellOR2;
int nDays = nCost / 1000; //one set of UMD checks per "day" spent crafting
if(nCost % 1000) nDays++;
sTemp = Get2DACache(sFile, "PrereqMisc", nLine);
sSpell = Get2DACache(sFile, "Spells", nLine);
if(sTemp == "")
{
bBreak = TRUE;
nRace = -1;
nAlignGE = -1;
nAlignLC = -1;
nClass = -1;
}
nLength = GetStringLength(sTemp);
for(i = 0; i < 5; i++)
{
if(bBreak)
break;
nPosition = FindSubString(sTemp, "_");
sSub = (nPosition == -1) ? sTemp : GetStringLeft(sTemp, nPosition);
nLength -= (nPosition + 1);
if(sSub == "*")
nTemp = -1;
else
nTemp = StringToInt(sSub);
switch(i)
{
case 0:
{
nRace = (MyPRCGetRacialType(oPC) == nTemp) ? -1 : nTemp;
break;
}
case 1:
{
//can't emulate feat requirement
break;
}
case 2:
{
nAlignGE = -1;
if(sSub == "G") nAlignGE = (GetAlignmentGoodEvil(oPC) == ALIGNMENT_GOOD) ? -1 : ALIGNMENT_GOOD;
else if(sSub == "E") nAlignGE = (GetAlignmentGoodEvil(oPC) == ALIGNMENT_EVIL) ? -1 : ALIGNMENT_EVIL;
else if(sSub == "N") nAlignGE = (GetAlignmentGoodEvil(oPC) == ALIGNMENT_NEUTRAL) ? -1 : ALIGNMENT_NEUTRAL;
break;
}
case 3:
{
nAlignLC = -1;
if(sSub == "L") nAlignLC = (GetAlignmentLawChaos(oPC) == ALIGNMENT_LAWFUL) ? -1 : ALIGNMENT_LAWFUL;
if(sSub == "C") nAlignLC = (GetAlignmentLawChaos(oPC) == ALIGNMENT_CHAOTIC) ? -1 : ALIGNMENT_CHAOTIC;
if(sSub == "N") nAlignLC = (GetAlignmentLawChaos(oPC) == ALIGNMENT_NEUTRAL) ? -1 : ALIGNMENT_NEUTRAL;
break;
}
case 4:
{
nClass = (GetLevelByClass(nTemp, oPC)) ? -1 : nTemp;
break;
}
}
sTemp = GetSubString(sTemp, nPosition + 1, nLength);
}
if(sSpell == "")
{
nSpell1 = -1;
nSpell2 = -1;
nSpell3 = -1;
nSpellOR1 = -1;
nSpellOR2 = -1;
}
else
{
for(i = 0; i < 5; i++)
{
nPosition = FindSubString(sTemp, "_");
sSub = (nPosition == -1) ? sTemp : GetStringLeft(sTemp, nPosition);
nLength -= (nPosition + 1);
if(sSub == "*")
nTemp = -1;
else
{
nTemp = StringToInt(sSub);
switch(i)
{
case 0:
{ //storing the spell level and assuming it's a valid number
nSpell1 = (PRCGetHasSpell(nTemp, oPC)) ? -1 : StringToInt(Get2DACache("spells", "Innate", nTemp)) + 20;
break;
}
case 1:
{
nSpell2 = (PRCGetHasSpell(nTemp, oPC)) ? -1 : StringToInt(Get2DACache("spells", "Innate", nTemp)) + 20;
break;
}
case 2:
{
nSpell3 = (PRCGetHasSpell(nTemp, oPC)) ? -1 : StringToInt(Get2DACache("spells", "Innate", nTemp)) + 20;
break;
}
case 3:
{
nSpellOR1 = (PRCGetHasSpell(nTemp, oPC)) ? -1 : StringToInt(Get2DACache("spells", "Innate", nTemp)) + 20;
break;
}
case 4:
{
nSpellOR2 = (PRCGetHasSpell(nTemp, oPC)) ? -1 : StringToInt(Get2DACache("spells", "Innate", nTemp)) + 20;
break;
}
}
}
sTemp = GetSubString(sTemp, nPosition + 1, nLength);
}
}
int bTake10 = GetHasFeat(FEAT_SKILL_MASTERY_ARTIFICER, oPC) ? 10 : -1;
for(i = 0; i <= nDays; i++) //with extra last-ditch roll
{
if((nRace == -1) &&
(nAlignGE == -1) &&
(nAlignLC == -1) &&
(nClass == -1) &&
(nSpell1 == -1) &&
(nSpell2 == -1) &&
(nSpell3 == -1) &&
(nSpellOR1 == -1) &&
(nSpellOR2 == -1)
)
return TRUE;
if(nRace != -1) nRace = (GetPRCIsSkillSuccessful(oPC, SKILL_USE_MAGIC_DEVICE, 25, bTake10)) ? -1 : nRace;
if(nAlignGE != -1) nAlignGE = (GetPRCIsSkillSuccessful(oPC, SKILL_USE_MAGIC_DEVICE, 30, bTake10)) ? -1 : nAlignGE;
if(nAlignLC != -1) nAlignLC = (GetPRCIsSkillSuccessful(oPC, SKILL_USE_MAGIC_DEVICE, 30, bTake10)) ? -1 : nAlignLC;
if(nClass != -1) nClass = (GetPRCIsSkillSuccessful(oPC, SKILL_USE_MAGIC_DEVICE, 21, bTake10)) ? -1 : nClass;
if(nSpell1 != -1) nSpell1 = (GetPRCIsSkillSuccessful(oPC, SKILL_USE_MAGIC_DEVICE, nSpell1, bTake10)) ? -1 : nSpell1;
if(nSpell2 != -1) nSpell2 = (GetPRCIsSkillSuccessful(oPC, SKILL_USE_MAGIC_DEVICE, nSpell2, bTake10)) ? -1 : nSpell2;
if(nSpell3 != -1) nSpell3 = (GetPRCIsSkillSuccessful(oPC, SKILL_USE_MAGIC_DEVICE, nSpell3, bTake10)) ? -1 : nSpell3;
if(nSpellOR1 != -1) nSpellOR1 = (GetPRCIsSkillSuccessful(oPC, SKILL_USE_MAGIC_DEVICE, nSpellOR1, bTake10)) ? -1 : nSpellOR1;
if(nSpellOR2 != -1) nSpellOR2 = (GetPRCIsSkillSuccessful(oPC, SKILL_USE_MAGIC_DEVICE, nSpellOR2, bTake10)) ? -1 : nSpellOR2;
}
if((nRace == -1) &&
(nAlignGE == -1) &&
(nAlignLC == -1) &&
(nClass == -1) &&
(nSpell1 == -1) &&
(nSpell2 == -1) &&
(nSpell3 == -1) &&
(nSpellOR1 == -1) &&
(nSpellOR2 == -1)
)
return TRUE;
else
return FALSE; //made all the UMD rolls allocated and still failed
}
// Save crafting state during crafting
void SaveCraftingState(object oPC)
{
if(DEBUG) DoDebug("prc_craft_cv_inc >> SaveCraftingState called for " + GetName(oPC));
if(GetLocalInt(oPC, PRC_CRAFT_HB))
{
if(DEBUG) DoDebug("prc_craft_cv_inc >> SaveCraftingState: Player is crafting, saving state...");
// Get the crafting item
object oItem = GetLocalObject(oPC, PRC_CRAFT_ITEM);
// Save item identification info
SQLocalsPlayer_SetString(oPC, "crafting_item_name", GetName(oItem));
SQLocalsPlayer_SetString(oPC, "crafting_item_tag", GetTag(oItem));
SQLocalsPlayer_SetInt(oPC, "crafting_item_basetype", GetBaseItemType(oItem));
// Save UUID if not already saved
string sItemUUID = SQLocalsPlayer_GetString(oPC, "crafting_item_uuid");
if(sItemUUID == "")
{
sItemUUID = GetObjectUUID(oItem);
SQLocalsPlayer_SetString(oPC, "crafting_item_uuid", sItemUUID);
if(DEBUG) DoDebug("prc_craft_cv_inc >> SaveCraftingState: Generated and saved new UUID: " + sItemUUID);
}
// Get crafting cost values
int nCostDiff = GetLocalInt(oPC, PRC_CRAFT_COST);
int nXPDiff = GetLocalInt(oPC, PRC_CRAFT_XP);
int nRounds = GetLocalInt(oPC, PRC_CRAFT_TIME);
// Save all basic crafting parameters
SQLocalsPlayer_SetInt(oPC, "crafting_cost", nCostDiff);
SQLocalsPlayer_SetInt(oPC, "crafting_xp", nXPDiff);
SQLocalsPlayer_SetInt(oPC, "crafting_rounds", nRounds);
SQLocalsPlayer_SetString(oPC, "crafting_file", GetLocalString(oPC, PRC_CRAFT_FILE));
SQLocalsPlayer_SetInt(oPC, "crafting_line", GetLocalInt(oPC, PRC_CRAFT_LINE));
SQLocalsPlayer_SetInt(oPC, "crafting_active", 1);
// Save item property components for reconstruction
SQLocalsPlayer_SetInt(oPC, "crafting_ip_type", GetLocalInt(oPC, "PRC_CRAFT_IP_TYPE"));
SQLocalsPlayer_SetInt(oPC, "crafting_ip_subtype", GetLocalInt(oPC, "PRC_CRAFT_IP_SUBTYPE"));
SQLocalsPlayer_SetInt(oPC, "crafting_ip_costtable", GetLocalInt(oPC, "PRC_CRAFT_IP_COSTTABLE"));
SQLocalsPlayer_SetInt(oPC, "crafting_ip_param1", GetLocalInt(oPC, "PRC_CRAFT_IP_PARAM1"));
// Save item property parameters
SQLocalsPlayer_SetInt(oPC, "crafting_type", GetLocalInt(oPC, PRC_CRAFT_TYPE));
SQLocalsPlayer_SetString(oPC, "crafting_subtype", GetLocalString(oPC, PRC_CRAFT_SUBTYPE));
SQLocalsPlayer_SetInt(oPC, "crafting_subtypevalue", GetLocalInt(oPC, PRC_CRAFT_SUBTYPEVALUE));
SQLocalsPlayer_SetString(oPC, "crafting_costtable", GetLocalString(oPC, PRC_CRAFT_COSTTABLE));
SQLocalsPlayer_SetInt(oPC, "crafting_costtablevalue", GetLocalInt(oPC, PRC_CRAFT_COSTTABLEVALUE));
SQLocalsPlayer_SetString(oPC, "crafting_param1", GetLocalString(oPC, PRC_CRAFT_PARAM1));
SQLocalsPlayer_SetInt(oPC, "crafting_param1value", GetLocalInt(oPC, PRC_CRAFT_PARAM1VALUE));
SQLocalsPlayer_SetInt(oPC, "crafting_proplist", GetLocalInt(oPC, PRC_CRAFT_PROPLIST));
// Save item properties
SQLocalsPlayer_SetInt(oPC, "crafting_baseitemtype", GetLocalInt(oPC, PRC_CRAFT_BASEITEMTYPE));
SQLocalsPlayer_SetInt(oPC, "crafting_time", nRounds);
SQLocalsPlayer_SetInt(oPC, "crafting_material", GetLocalInt(oPC, PRC_CRAFT_MATERIAL));
SQLocalsPlayer_SetInt(oPC, "crafting_mighty", GetLocalInt(oPC, PRC_CRAFT_MIGHTY));
SQLocalsPlayer_SetInt(oPC, "crafting_ac", GetLocalInt(oPC, PRC_CRAFT_AC));
SQLocalsPlayer_SetString(oPC, "crafting_tag", GetLocalString(oPC, PRC_CRAFT_TAG));
// Save magic crafting variables
SQLocalsPlayer_SetInt(oPC, "crafting_enhancement", GetLocalInt(oPC, PRC_CRAFT_MAGIC_ENHANCE));
SQLocalsPlayer_SetInt(oPC, "crafting_additional", GetLocalInt(oPC, PRC_CRAFT_MAGIC_ADDITIONAL));
SQLocalsPlayer_SetInt(oPC, "crafting_epic", GetLocalInt(oPC, PRC_CRAFT_MAGIC_EPIC));
// Save system state variables
SQLocalsPlayer_SetInt(oPC, "crafting_script_state", GetLocalInt(oPC, PRC_CRAFT_SCRIPT_STATE));
SQLocalsPlayer_SetInt(oPC, "crafting_token", GetLocalInt(oPC, PRC_CRAFT_TOKEN));
if(DEBUG) DoDebug("DEBUG: Crafting state saved - rounds: " + IntToString(nRounds));
}
}
/* // Save crafting state on logout, pause concentration checks & time tracking
void SaveCraftingState(object oPC)
{
if(GetLocalInt(oPC, "PRC_CRAFT_HB"))
{
// Get all crafting parameters
object oItem = GetLocalObject(oPC, "PRC_CRAFT_ITEM");
int nCost = GetLocalInt(oPC, "PRC_CRAFT_COST");
int nXP = GetLocalInt(oPC, "PRC_CRAFT_XP");
int nRounds = GetLocalInt(oPC, "PRC_CRAFT_ROUNDS");
string sFile = GetLocalString(oPC, "PRC_CRAFT_FILE");
int nLine = GetLocalInt(oPC, "PRC_CRAFT_LINE");
// Save all parameters to database
SQLocalsPlayer_SetObject(oPC, "crafting_item", oItem);
SQLocalsPlayer_SetInt(oPC, "crafting_cost", nCost);
SQLocalsPlayer_SetInt(oPC, "crafting_xp", nXP);
SQLocalsPlayer_SetInt(oPC, "crafting_rounds", nRounds);
SQLocalsPlayer_SetString(oPC, "crafting_file", sFile);
SQLocalsPlayer_SetInt(oPC, "crafting_line", nLine);
SQLocalsPlayer_SetInt(oPC, "crafting_active", 1);
// Save logout time using PRC time system
struct time tLogoutTime = GetTimeAndDate();
SetPersistantLocalTime(oPC, "crafting_logout_time", tLogoutTime);
// Remove concentration monitoring and clear heartbeat
RemoveEventScript(oPC, EVENT_VIRTUAL_ONDAMAGED, "prc_od_conc");
DeleteLocalInt(oPC, "PRC_CRAFT_HB");
}
} */
object GetItemByUUID(object oPC, string sUUID)
{
if(DEBUG) DoDebug("prc_craft_cv_inc >> GetItemByUUID | Searching for item with UUID: " + sUUID);
// Check player's inventory
object oItem = GetFirstItemInInventory(oPC);
while(GetIsObjectValid(oItem))
{
if(GetObjectUUID(oItem) == sUUID)
{
if(DEBUG) DoDebug("prc_craft_cv_inc >> GetItemByUUID | Found item in player inventory: " + GetName(oItem));
return oItem;
}
oItem = GetNextItemInInventory(oPC);
}
// Check equipped slots
int i;
for(i = 0; i < NUM_INVENTORY_SLOTS; i++)
{
oItem = GetItemInSlot(i, oPC);
if(GetIsObjectValid(oItem) && GetObjectUUID(oItem) == sUUID)
{
if(DEBUG) DoDebug("prc_craft_cv_inc >> GetItemByUUID | Found equipped item: " + GetName(oItem));
return oItem;
}
}
// Check the craft storage chest
object oChest = GetCraftChest();
if(GetIsObjectValid(oChest))
{
oItem = GetFirstItemInInventory(oChest);
while(GetIsObjectValid(oItem))
{
if(GetObjectUUID(oItem) == sUUID)
{
if(DEBUG) DoDebug("prc_craft_cv_inc >> GetItemByUUID | Found item in craft chest: " + GetName(oItem));
return oItem;
}
oItem = GetNextItemInInventory(oChest);
}
}
// Check temporary craft chest
object oTempChest = GetTempCraftChest();
if(GetIsObjectValid(oTempChest))
{
oItem = GetFirstItemInInventory(oTempChest);
while(GetIsObjectValid(oItem))
{
if(GetObjectUUID(oItem) == sUUID)
{
if(DEBUG) DoDebug("prc_craft_cv_inc >> GetItemByUUID | Found item in temp craft chest: " + GetName(oItem));
return oItem;
}
oItem = GetNextItemInInventory(oTempChest);
}
}
if(DEBUG) DoDebug("prc_craft_cv_inc >> GetItemByUUID | Item not found with UUID: " + sUUID);
return OBJECT_INVALID;
}
void CraftingHB(object oPC, object oItem, itemproperty ip, int nCost, int nXP, string sFile, int nLine, int nRounds)
{
// Save current timestamp for offline progress calculation
int nCurrentTime = GetCurrentUnixTimestamp();
if(nCurrentTime > 0)
{
SQLocalsPlayer_SetInt(oPC, "crafting_last_timestamp", nCurrentTime);
}
// Set the heartbeat flag
SetLocalInt(oPC, PRC_CRAFT_HB, 1);
// Set database flag for persistence tracking
SQLocalsPlayer_SetInt(oPC, "crafting_active", 1);
// Update local variables for heartbeat logic
SetLocalInt(oPC, PRC_CRAFT_TIME, nRounds);
SetLocalInt(oPC, PRC_CRAFT_COST, nCost);
SetLocalInt(oPC, PRC_CRAFT_XP, nXP);
SetLocalString(oPC, PRC_CRAFT_FILE, sFile);
SetLocalInt(oPC, PRC_CRAFT_LINE, nLine);
// Store the item property components for reconstruction
if(GetIsItemPropertyValid(ip))
{
SetLocalInt(oPC, "PRC_CRAFT_IP_TYPE", GetItemPropertyType(ip));
SetLocalInt(oPC, "PRC_CRAFT_IP_SUBTYPE", GetItemPropertySubType(ip));
SetLocalInt(oPC, "PRC_CRAFT_IP_COSTTABLE", GetItemPropertyCostTableValue(ip));
SetLocalInt(oPC, "PRC_CRAFT_IP_PARAM1", GetItemPropertyParam1Value(ip));
}
// Save current crafting state continuously for persistence
if(GetPRCSwitch(PRC_CRAFTING_TIME_SCALE) > 1)
{
// Store the CURRENT rounds value in database
SQLocalsPlayer_SetInt(oPC, "crafting_rounds", nRounds);
SaveCraftingState(oPC);
}
if(GetBreakConcentrationCheck(oPC))
{
FloatingTextStringOnCreature("Crafting: Concentration lost!", oPC);
DeleteLocalInt(oPC, PRC_CRAFT_HB);
RemoveEventScript(oPC, EVENT_VIRTUAL_ONDAMAGED, "prc_od_conc");
// Clear database state
SQLocalsPlayer_SetInt(oPC, "crafting_active", 0);
return;
}
if(nRounds == 0 || GetPCPublicCDKey(oPC) == "") //default to zero time if single player
{
if(DEBUG) DoDebug("prc_craft_cv_inc >> CraftHB() | Crafting completion - nCost: " + IntToString(nCost) + ", nLine: " + IntToString(nLine));
RemoveEventScript(oPC, EVENT_VIRTUAL_ONDAMAGED, "prc_od_conc");
if(GetLevelByClass(CLASS_TYPE_ARTIFICER, oPC))
{
if(!ArtificerPrereqCheck(oPC, sFile, nLine, nCost))
{
FloatingTextStringOnCreature("Crafting Failed!", oPC);
DeleteLocalInt(oPC, PRC_CRAFT_HB);
TakeGoldFromCreature(nCost, oPC, TRUE);
SetXP(oPC, PRCMax(GetXP(oPC) - nXP, GetHitDice(oPC) * (GetHitDice(oPC) - 1) * 500));
SQLocalsPlayer_SetInt(oPC, "crafting_active", 0);
return;
}
}
FloatingTextStringOnCreature("Crafting Complete!", oPC);
if(DEBUG) DoDebug("prc_craft_cv_inc >> CraftHB() | Entering ApplyProperties() - nCost: " + IntToString(nCost) + ", nLine: " + IntToString(nLine));
ApplyProperties(oPC, oItem, ip, nCost, nXP, sFile, nLine);
DeleteLocalInt(oPC, PRC_CRAFT_HB);
if(GetLocalInt(oPC, "PRC_CRAFT_REMOVE_MASTERWORK"))
{
RemoveMasterworkProperties(oItem);
DeleteLocalInt(oPC, "PRC_CRAFT_REMOVE_MASTERWORK");
}
// Clear database state on completion
SQLocalsPlayer_SetInt(oPC, "crafting_active", 0);
}
else
{
if(DEBUG) DoDebug("prc_craft_cv_inc >> CraftHB() | Continuing CraftingHB() - nCost: " + IntToString(nCost) + ", nLine: " + IntToString(nLine));
FloatingTextStringOnCreature("Crafting: " + IntToString(nRounds) + " round(s) remaining", oPC);
DelayCommand(6.0, CraftingHB(oPC, oItem, ip, nCost, nXP, sFile, nLine, nRounds - 1));
}
}
//;: void main(){}