2026/01/17 Update
Removed a lot of old backup files. Fixed Eye of Gruumsh's epic bonus feats. Add Epic marker feat for Eye of Gruumsh. Added Channeled Pyroburst to prc_desc_fire.2da Added GetCurrentUnixTimestamp() function. Moved crafting conversation functions to prc_craft_cv_inc.nss. Made Midnight Augment work slightly better, still not quite per PnP yet. Disciple of Asmodeus' Summoned Devils are supposed to be Lawful Evil. Every instance of ItemPropertySpellImmunitySpecific() in race_skin.nss was misconfigured. Several instances of ItemPropertyDamageImmunity() in race_skin.nss were misconfigured. Fixed issue where Blighters were still considered undead after leaving undead wildshape. PRC8 now supports offline PnP magical crafting. Disciple of Asmodeus' Dread Night now increases AC instead of Damage, per PnP. Non-spellcaster Disciples of Asmodeus have a Hellcat duration based on DoA class level. Hexblade's Dark Companion shouldn't lose Sacntuary when loading from a save. Claws of the Savage should increase size properly if caster already has claws at time of casting.
This commit is contained in:
@@ -56,8 +56,10 @@ void main()
|
||||
PRCRemoveSpellEffects(2285, oPC, oPC);
|
||||
PRCRemoveSpellEffects(2286, oPC, oPC);
|
||||
PRCRemoveSpellEffects(2287, oPC, oPC);
|
||||
PRCRemoveSpellEffects(2288, oPC, oPC);
|
||||
PRCRemoveSpellEffects(2288, oPC, oPC);
|
||||
|
||||
DeleteLocalInt(oPC, "PRC_ShiftingOverride_Race");
|
||||
|
||||
if(GetLocalInt(oPC, "AraneaHumanoidForm") == TRUE || GetLocalInt(oPC, "AraneaHybridForm") == TRUE || GetPersistantLocalInt(oPC, "nPCShifted") == TRUE || GetLocalInt(oPC, "shifting") == TRUE)
|
||||
{
|
||||
SetLocalInt(oPC, "AraneaBiteEquip", TRUE);
|
||||
|
||||
@@ -22,808 +22,9 @@
|
||||
89 properties with constants, 15 without
|
||||
*/
|
||||
|
||||
#include "prc_craft_inc"
|
||||
#include "prc_craft_cv_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(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(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;
|
||||
}
|
||||
}
|
||||
ApplyItemProps(oItem, sFile, nLine);
|
||||
}
|
||||
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_BREACH), oPC);
|
||||
TakeGoldFromCreature(nCost, oPC, TRUE);
|
||||
SetXP(oPC, GetXP(oPC) - nXP);
|
||||
}
|
||||
|
||||
//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
|
||||
}
|
||||
|
||||
void CraftingHB(object oPC, object oItem, itemproperty ip, int nCost, int nXP, string sFile, int nLine, int nRounds)
|
||||
{
|
||||
if(GetBreakConcentrationCheck(oPC))
|
||||
{
|
||||
FloatingTextStringOnCreature("Crafting: Concentration lost!", oPC);
|
||||
DeleteLocalInt(oPC, PRC_CRAFT_HB);
|
||||
RemoveEventScript(oPC, EVENT_VIRTUAL_ONDAMAGED, "prc_od_conc");
|
||||
return;
|
||||
}
|
||||
if(nRounds == 0 || GetPCPublicCDKey(oPC) == "") //default to zero time if single player
|
||||
{
|
||||
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)); //can't delevel
|
||||
return;
|
||||
}
|
||||
}
|
||||
FloatingTextStringOnCreature("Crafting Complete!", oPC);
|
||||
DeleteLocalInt(oPC, PRC_CRAFT_HB);
|
||||
//if(GetIsItemPropertyValid(ip))
|
||||
ApplyProperties(oPC, oItem, ip, nCost, nXP, sFile, nLine);
|
||||
|
||||
if(GetLocalInt(oPC, "PRC_CRAFT_REMOVE_MASTERWORK"))
|
||||
{
|
||||
RemoveMasterworkProperties(oItem);
|
||||
DeleteLocalInt(oPC, "PRC_CRAFT_REMOVE_MASTERWORK");
|
||||
}
|
||||
|
||||
if(sFile == "craft_golem")
|
||||
{
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FloatingTextStringOnCreature("Crafting: " + IntToString(nRounds) + " round(s) remaining", oPC);
|
||||
DelayCommand(6.0, CraftingHB(oPC, oItem, ip, nCost, nXP, sFile, nLine, nRounds - 1));
|
||||
}
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
@@ -1882,6 +1083,17 @@ void main()
|
||||
TakeGoldFromCreature(GetLocalInt(oPC, PRC_CRAFT_COST), oPC, TRUE);
|
||||
if(GetCraftingFeat(oNewItem) != FEAT_CRAFT_ARMS_ARMOR)
|
||||
{
|
||||
// Add this in prc_craft.nss where crafting is confirmed
|
||||
int nTimeScale = GetPRCSwitch(PRC_CRAFTING_TIME_SCALE);
|
||||
SendMessageToPC(oPC, "DEBUG: PRC_CRAFTING_TIME_SCALE = " + IntToString(nTimeScale));
|
||||
|
||||
int nBaseTime = GetCraftingTime(nCost);
|
||||
SendMessageToPC(oPC, "DEBUG: Base crafting time = " + IntToString(nBaseTime) + " rounds");
|
||||
|
||||
int nFeat = GetCraftingFeat(oItem);
|
||||
int nModifiedTime = GetModifiedTimeCost(nBaseTime, oPC, nFeat);
|
||||
SendMessageToPC(oPC, "DEBUG: Modified crafting time = " + IntToString(nModifiedTime) + " rounds");
|
||||
|
||||
SetLocalInt(oPC, "PRC_CRAFT_REMOVE_MASTERWORK", TRUE);
|
||||
CopyItem(oNewItem, oPC, TRUE);
|
||||
}
|
||||
@@ -1907,12 +1119,37 @@ void main()
|
||||
nStage = STAGE_BANE;
|
||||
MarkStageNotSetUp(nStage, oPC);
|
||||
}
|
||||
else if(nChoice == CHOICE_CONFIRM)
|
||||
{
|
||||
SetLocalInt(oPC, PRC_CRAFT_HB, 1);
|
||||
CraftingHB(oPC, oItem, ip, nCost, nXP, sFile, nLine, nTime);
|
||||
AllowExit(DYNCONV_EXIT_FORCE_EXIT);
|
||||
}
|
||||
else if(nChoice == CHOICE_CONFIRM)
|
||||
{
|
||||
// Save the item's UUID when crafting begins
|
||||
object oItem = GetLocalObject(oPC, PRC_CRAFT_ITEM);
|
||||
string sItemUUID = GetObjectUUID(oItem);
|
||||
SQLocalsPlayer_SetString(oPC, "crafting_item_uuid", sItemUUID);
|
||||
SQLocalsPlayer_SetString(oPC, "crafting_item_name", GetName(oItem));
|
||||
|
||||
// Construct the item property for this crafting session
|
||||
int nType = GetLocalInt(oPC, PRC_CRAFT_TYPE);
|
||||
int nSubTypeValue = GetLocalInt(oPC, PRC_CRAFT_SUBTYPEVALUE);
|
||||
int nCostTableValue = GetLocalInt(oPC, PRC_CRAFT_COSTTABLEVALUE);
|
||||
int nParam1Value = GetLocalInt(oPC, PRC_CRAFT_PARAM1VALUE);
|
||||
|
||||
itemproperty ip;
|
||||
if(nType > 0)
|
||||
{
|
||||
ip = ConstructIP(nType, nSubTypeValue, nCostTableValue, nParam1Value);
|
||||
}
|
||||
|
||||
// Store IP components for persistence
|
||||
SetLocalInt(oPC, "PRC_CRAFT_IP_TYPE", nType);
|
||||
SetLocalInt(oPC, "PRC_CRAFT_IP_SUBTYPE", nSubTypeValue);
|
||||
SetLocalInt(oPC, "PRC_CRAFT_IP_COSTTABLE", nCostTableValue);
|
||||
SetLocalInt(oPC, "PRC_CRAFT_IP_PARAM1", nParam1Value);
|
||||
|
||||
SetLocalInt(oPC, PRC_CRAFT_HB, 1);
|
||||
SQLocalsPlayer_SetInt(oPC, "crafting_active", 1);
|
||||
CraftingHB(oPC, oItem, ip, nCost, nXP, sFile, nLine, nTime);
|
||||
AllowExit(DYNCONV_EXIT_FORCE_EXIT);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case STAGE_APPEARANCE:
|
||||
|
||||
@@ -13,10 +13,10 @@ void main()
|
||||
object oPC = PRCGetSpellTargetObject();
|
||||
object oWeap = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC);
|
||||
|
||||
effect eDam = EffectDamageIncrease(DAMAGE_BONUS_2, DAMAGE_TYPE_DIVINE);
|
||||
effect eAC = EffectACIncrease(2, AC_DODGE_BONUS);
|
||||
effect eAtk = EffectAttackIncrease(2);
|
||||
effect eSave = EffectSavingThrowIncrease(SAVING_THROW_ALL, 2);
|
||||
effect eLink = EffectLinkEffects(eDam, eAtk);
|
||||
effect eLink = EffectLinkEffects(eAC, eAtk);
|
||||
eLink = EffectLinkEffects(eLink, eSave);
|
||||
eLink = ExtraordinaryEffect(eLink);
|
||||
|
||||
|
||||
@@ -21,6 +21,11 @@ void main()
|
||||
effect eVis = EffectVisualEffect(VFX_FNF_SUMMON_GATE);
|
||||
float fDuration = HoursToSeconds(24);
|
||||
int nDuration = PRCGetCasterLevel(oPC);
|
||||
if (nDuration == 0)
|
||||
{
|
||||
nDuration = 2 * GetLevelByClass(CLASS_TYPE_DISCIPLE_OF_ASMODEUS, oPC);
|
||||
}
|
||||
|
||||
if(GetPRCSwitch(PRC_SUMMON_ROUND_PER_LEVEL))
|
||||
fDuration = RoundsToSeconds(nDuration*GetPRCSwitch(PRC_SUMMON_ROUND_PER_LEVEL));
|
||||
|
||||
|
||||
@@ -7,6 +7,12 @@
|
||||
* Specifics: The Hexblade gains a dark companion. It is an illusionary creature that does not engage in combat, but all monsters near it take a -2 penalty to AC and Saves.
|
||||
* Use: Selected.
|
||||
*/
|
||||
|
||||
//::
|
||||
//:: Updated by: Jaysyn
|
||||
//;; Updated on: 2026-01-16 00:11:27
|
||||
//::
|
||||
|
||||
#include "prc_inc_assoc"
|
||||
|
||||
void main()
|
||||
@@ -28,7 +34,7 @@ void main()
|
||||
eLink = EffectLinkEffects(eLink, EffectVisualEffect(VFX_DUR_GLOW_GREY));//VFX_DUR_PROT_PRC_SHADOW_ARMOR
|
||||
eLink = EffectLinkEffects(eLink, EffectCutsceneGhost());
|
||||
eLink = EffectLinkEffects(eLink, EffectEthereal());
|
||||
eLink = SupernaturalEffect(eLink);
|
||||
eLink = UnyieldingEffect(eLink);
|
||||
|
||||
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eLink, oCompanion);
|
||||
}
|
||||
@@ -9,6 +9,107 @@
|
||||
#include "shd_inc_myst"
|
||||
#include "prc_inc_template"
|
||||
#include "prc_inc_factotum"
|
||||
#include "inc_persistsql"
|
||||
#include "prc_craft_cv_inc"
|
||||
|
||||
//:: Restore crafting state on login with offline time calculation
|
||||
void RestoreCraftingStateOnLogin(object oPC)
|
||||
{
|
||||
if(DEBUG) DoDebug("DEBUG: RestoreCraftingStateOnLogin called for " + GetName(oPC));
|
||||
|
||||
// Check switch conditions
|
||||
if(!(!GetPRCSwitch(PRC_DISABLE_CRAFT) &&
|
||||
GetPRCSwitch(PRC_CRAFTING_TIME_SCALE) > 1))
|
||||
{
|
||||
if(DEBUG) DoDebug("DEBUG: Switch conditions not met for crafting restore");
|
||||
return;
|
||||
}
|
||||
|
||||
if(DEBUG) DoDebug("DEBUG: Switch conditions met, checking for saved crafting state");
|
||||
|
||||
if(SQLocalsPlayer_GetInt(oPC, "crafting_active"))
|
||||
{
|
||||
if(DEBUG) DoDebug("DEBUG: Found active crafting state, restoring...");
|
||||
|
||||
// Get basic crafting state
|
||||
string sUUID = SQLocalsPlayer_GetString(oPC, "crafting_item_uuid");
|
||||
int nRounds = SQLocalsPlayer_GetInt(oPC, "crafting_rounds");
|
||||
int nCost = SQLocalsPlayer_GetInt(oPC, "crafting_cost");
|
||||
int nXP = SQLocalsPlayer_GetInt(oPC, "crafting_xp");
|
||||
string sFile = SQLocalsPlayer_GetString(oPC, "crafting_file");
|
||||
int nLine = SQLocalsPlayer_GetInt(oPC, "crafting_line");
|
||||
int nIPType = SQLocalsPlayer_GetInt(oPC, "crafting_ip_type");
|
||||
int nIPSubtype = SQLocalsPlayer_GetInt(oPC, "crafting_ip_subtype");
|
||||
int nIPCostTable = SQLocalsPlayer_GetInt(oPC, "crafting_ip_costtable");
|
||||
int nIPParam1 = SQLocalsPlayer_GetInt(oPC, "crafting_ip_param1");
|
||||
|
||||
if(DEBUG) DoDebug("DEBUG: Initial data - UUID: " + sUUID + ", rounds: " + IntToString(nRounds) +
|
||||
", cost: " + IntToString(nCost) + ", xp: " + IntToString(nXP));
|
||||
|
||||
// Calculate offline progress
|
||||
int nLogoutTime = SQLocalsPlayer_GetInt(oPC, "crafting_last_timestamp");
|
||||
int nCurrentTime = GetCurrentUnixTimestamp();
|
||||
if(DEBUG) DoDebug("prc_onenter >> : RestoreCraftingStateOnLogin() | GetCurrentUnixTimestamp is:" + IntToString(nCurrentTime) +".");
|
||||
|
||||
if(nLogoutTime > 0 && nCurrentTime > nLogoutTime)
|
||||
{
|
||||
// Calculate real time elapsed in seconds
|
||||
int nSecondsOffline = nCurrentTime - nLogoutTime;
|
||||
if(DEBUG) DoDebug("prc_onenter >> : RestoreCraftingStateOnLogin() | nSecondsOffline is:" + IntToString(nSecondsOffline) +".");
|
||||
|
||||
// Each round is always 6 seconds real time
|
||||
int nRoundsOffline = nSecondsOffline / 6;
|
||||
if(DEBUG) DoDebug("prc_onenter >> : RestoreCraftingStateOnLogin() | nRoundsOffline is:" + IntToString(nRoundsOffline) +".");
|
||||
|
||||
// Subtract offline progress from remaining rounds
|
||||
nRounds -= nRoundsOffline;
|
||||
if(nRounds < 1) nRounds = 1; // Minimum 1 round to finish
|
||||
|
||||
if(DEBUG) DoDebug("DEBUG: Offline progress - time diff: " + IntToString(nSecondsOffline) +
|
||||
"s, rounds progress: " + IntToString(nRoundsOffline) +
|
||||
", new rounds: " + IntToString(nRounds));
|
||||
}
|
||||
else
|
||||
{
|
||||
if(DEBUG) DoDebug("DEBUG: No valid logout time found, using saved rounds: " + IntToString(nRounds));
|
||||
}
|
||||
|
||||
// Find the crafting item
|
||||
object oItem = GetItemByUUID(oPC, sUUID);
|
||||
if(GetIsObjectValid(oItem))
|
||||
{
|
||||
if(DEBUG) DoDebug("DEBUG: Found item, restoring crafting session");
|
||||
|
||||
// Reconstruct the itemproperty
|
||||
itemproperty ip;
|
||||
if(nIPType > 0)
|
||||
{
|
||||
ip = ConstructIP(nIPType, nIPSubtype, nIPCostTable, nIPParam1);
|
||||
}
|
||||
|
||||
if(DEBUG) DoDebug("DEBUG: About to call CraftingHB with " + IntToString(nRounds) + " rounds, cost: " + IntToString(nCost) + ", xp: " + IntToString(nXP));
|
||||
|
||||
// Notify player
|
||||
FloatingTextStringOnCreature("Resuming crafting session: " + IntToString(nRounds) + " round(s) remaining", oPC);
|
||||
|
||||
// Restart the crafting heartbeat with all correct parameters
|
||||
AssignCommand(oPC, ClearAllActions(TRUE));
|
||||
SetLocalInt(oPC, "PRC_CRAFT_RESTORED", 1);
|
||||
DelayCommand(3.0, CraftingHB(oPC, oItem, ip, nCost, nXP, sFile, nLine, nRounds));
|
||||
}
|
||||
else
|
||||
{
|
||||
if(DEBUG) DoDebug("DEBUG: Failed to find item with UUID: " + sUUID);
|
||||
FloatingTextStringOnCreature("Crafting session could not be restored - item not found", oPC);
|
||||
// Clear the invalid crafting state
|
||||
SQLocalsPlayer_SetInt(oPC, "crafting_active", 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(DEBUG) DoDebug("DEBUG: No saved crafting state found");
|
||||
}
|
||||
}
|
||||
|
||||
void RestoreForsakerAbilities(object oPC)
|
||||
{
|
||||
@@ -160,6 +261,10 @@ void main()
|
||||
//hopefully in the next update
|
||||
// -Aaon Graywolf
|
||||
object oPC = GetEnteringObject();
|
||||
|
||||
if(DEBUG) DoDebug("onEnter DEBUG: PW_LOC=" + IntToString(GetPRCSwitch(PRC_PW_LOCATION_TRACKING)) +
|
||||
" DISABLE_CRAFT=" + IntToString(GetPRCSwitch(PRC_DISABLE_CRAFT)) +
|
||||
" TIME_SCALE=" + IntToString(GetPRCSwitch(PRC_CRAFTING_TIME_SCALE)));
|
||||
|
||||
//FloatingTextStringOnCreature("PRC on enter was called", oPC, FALSE);
|
||||
|
||||
@@ -448,4 +553,17 @@ void main()
|
||||
// Start the PC HeartBeat for PC's
|
||||
if(GetIsPC(oPC))
|
||||
AssignCommand(GetModule(), ExecuteScript("prc_onhb_indiv", oPC));
|
||||
|
||||
// Only restore crafting state if PW features and PnP crafting are enabled
|
||||
if(!GetPRCSwitch(PRC_DISABLE_CRAFT) &&
|
||||
GetPRCSwitch(PRC_CRAFTING_TIME_SCALE) > 1)
|
||||
{
|
||||
if(DEBUG) DoDebug("prc_onenter: Calling RestoreCraftingStateOnLogin in 6 seconds.");
|
||||
DelayCommand(6.0, RestoreCraftingStateOnLogin(oPC));
|
||||
//if(DEBUG) DoDebug("DEBUG: About to call RestoreCraftingStateOnLogin immediately");
|
||||
//RestoreCraftingStateOnLogin(oPC);
|
||||
}
|
||||
|
||||
//:: Display PRC8 version
|
||||
FloatingTextStringOnCreature("PRC8 Version: " + PRC_VERSION, oPC, FALSE);
|
||||
}
|
||||
@@ -4,11 +4,82 @@
|
||||
//:://////////////////////////////////////////////
|
||||
#include "prc_class_const"
|
||||
#include "inc_utility"
|
||||
#include "inc_persistsql"
|
||||
|
||||
// Save logout time when actually logging out
|
||||
void SaveCraftingLogoutTime(object oPC)
|
||||
{
|
||||
if(DEBUG) DoDebug("SaveCraftingLogoutTime: Called for " + GetName(oPC));
|
||||
|
||||
// Check database first, then local int
|
||||
int bCrafting = SQLocalsPlayer_GetInt(oPC, "crafting_active");
|
||||
if(!bCrafting)
|
||||
{
|
||||
bCrafting = GetLocalInt(oPC, "PRC_CRAFT_HB");
|
||||
if(DEBUG) DoDebug("SaveCraftingLogoutTime: DB crafting_active = " + IntToString(bCrafting) +
|
||||
", Local PRC_CRAFT_HB = " + IntToString(GetLocalInt(oPC, "PRC_CRAFT_HB")));
|
||||
}
|
||||
|
||||
if(bCrafting)
|
||||
{
|
||||
// Save the logout timestamp with fallback
|
||||
int nLogoutTimestamp = GetCurrentUnixTimestamp();
|
||||
if(DEBUG) DoDebug("prc_onleave >> SaveCraftingLogoutTime | nLogoutTimestamp is: " + IntToString(nLogoutTimestamp));
|
||||
|
||||
if(nLogoutTimestamp == 0)
|
||||
{
|
||||
// Fallback: use a simple counter
|
||||
int nLastLogout = SQLocalsPlayer_GetInt(oPC, "crafting_last_timestamp");
|
||||
nLogoutTimestamp = nLastLogout + 1;
|
||||
SQLocalsPlayer_SetInt(oPC, "crafting_last_timestamp", nLogoutTimestamp);
|
||||
if(DEBUG) DoDebug("SaveCraftingLogoutTime: Using fallback counter: " + IntToString(nLogoutTimestamp));
|
||||
}
|
||||
|
||||
// Save the timestamp
|
||||
SQLocalsPlayer_SetInt(oPC, "crafting_last_timestamp", nLogoutTimestamp);
|
||||
|
||||
// Save current rounds remaining
|
||||
int nRounds = GetLocalInt(oPC, "PRC_CRAFT_TIME");
|
||||
SQLocalsPlayer_SetInt(oPC, "crafting_rounds", nRounds);
|
||||
|
||||
// Save the crafting costs
|
||||
SQLocalsPlayer_SetInt(oPC, "crafting_cost", GetLocalInt(oPC, "PRC_CRAFT_COST"));
|
||||
SQLocalsPlayer_SetInt(oPC, "crafting_xp", GetLocalInt(oPC, "PRC_CRAFT_XP"));
|
||||
|
||||
if(nRounds > 0)
|
||||
{
|
||||
SQLocalsPlayer_SetInt(oPC, "crafting_rounds", nRounds);
|
||||
}
|
||||
|
||||
if(DEBUG) DoDebug("SaveCraftingLogoutTime: Saved logout timestamp " + IntToString(nLogoutTimestamp) + ", rounds remaining: " + IntToString(nRounds));
|
||||
|
||||
SQLocalsPlayer_SetString(oPC, "crafting_file", GetLocalString(oPC, "PRC_CRAFT_FILE"));
|
||||
SQLocalsPlayer_SetInt(oPC, "crafting_line", GetLocalInt(oPC, "PRC_CRAFT_LINE"));
|
||||
|
||||
// Remove concentration monitoring and clear heartbeat
|
||||
RemoveEventScript(oPC, EVENT_VIRTUAL_ONDAMAGED, "prc_od_conc");
|
||||
DeleteLocalInt(oPC, "PRC_CRAFT_HB");
|
||||
}
|
||||
else
|
||||
{
|
||||
if(DEBUG) DoDebug("SaveCraftingLogoutTime: Player not crafting, skipping");
|
||||
}
|
||||
}
|
||||
void main()
|
||||
{
|
||||
//:: Execute scripts hooked to this event for the player triggering it
|
||||
//:: Execute scripts hooked to this event for the player triggering it
|
||||
object oPC = GetExitingObject();
|
||||
AssignCommand(GetModule(), DelayCommand(0.1, RecalculateTime()));
|
||||
string sPCName = GetName(oPC);
|
||||
|
||||
if (oPC == OBJECT_INVALID)
|
||||
{
|
||||
DoDebug("prc_onleave: oPC "+sPCName+" is invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
if(DEBUG) DoDebug("prc_onleave: Player " +sPCName+ " leaving");
|
||||
|
||||
AssignCommand(GetModule(), DelayCommand(0.1, RecalculateTime()));
|
||||
//SaveCraftingLogoutTime(oPC);
|
||||
ExecuteAllScriptsHookedToEvent(oPC, EVENT_ONCLIENTLEAVE);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user