//::////////////////////////////////////////////// //:: Ultimate Magus Spell Knowledge choice script //:: prc_um_eskcon //::////////////////////////////////////////////// /* @author Stratovarius - 2019.12.29 */ //::////////////////////////////////////////////// //::////////////////////////////////////////////// #include "prc_inc_spells" #include "inc_dynconv" ////////////////////////////////////////////////// /* Constant definitions */ ////////////////////////////////////////////////// const int STAGE_SELECT_SPELL = 0; const int STAGE_CONFIRM_SELECTION_SPELL = 1; const int STAGE_SELECT_SPELL_LEVEL = 2; const int CHOICE_BACK_TO_LSELECT = -1; const int STRREF_SELECTED_HEADER1 = 16824209; // "You have selected:" const int STRREF_SELECTED_HEADER2 = 16824210; // "Is this correct?" const int LEVEL_STRREF_START = 16824809; const int STRREF_YES = 4752; // "Yes" const int STRREF_NO = 4753; // "No" const int SORT = TRUE; // If the sorting takes too much CPU, set to FALSE const int DEBUG_LIST = FALSE; ////////////////////////////////////////////////// /* Function defintions */ ////////////////////////////////////////////////// void PrintList(object oMagus) { string tp = "Printing list:\n"; string s = GetLocalString(oMagus, "PRC_EssentiaConvo_List_Head"); if(s == ""){ tp += "Empty\n"; } else{ tp += s + "\n"; s = GetLocalString(oMagus, "PRC_EssentiaConvo_List_Next_" + s); while(s != ""){ tp += "=> " + s + "\n"; s = GetLocalString(oMagus, "PRC_EssentiaConvo_List_Next_" + s); } } DoDebug(tp); } /** * Creates a linked list of entries that is sorted into alphabetical order * as it is built. * Assumption: mystery names are unique. * * @param oMagus The storage object aka whomever is gaining powers in this conversation * @param sChoice The choice string * @param nChoice The choice value */ void AddToTempList(object oMagus, string sChoice, int nChoice) { if(DEBUG_LIST) DoDebug("\nAdding to temp list: '" + sChoice + "' - " + IntToString(nChoice)); if(DEBUG_LIST) PrintList(oMagus); // If there is nothing yet if(!GetLocalInt(oMagus, "PRC_EssentiaConvo_ListInited")) { SetLocalString(oMagus, "PRC_EssentiaConvo_List_Head", sChoice); SetLocalInt(oMagus, "PRC_EssentiaConvo_List_" + sChoice, nChoice); SetLocalInt(oMagus, "PRC_EssentiaConvo_ListInited", TRUE); } else { // Find the location to instert into string sPrev = "", sNext = GetLocalString(oMagus, "PRC_EssentiaConvo_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(oMagus, "PRC_EssentiaConvo_List_Next_" + sNext); } // Insert the new entry // Does it replace the head? if(sPrev == "") { if(DEBUG_LIST) DoDebug("New head"); SetLocalString(oMagus, "PRC_EssentiaConvo_List_Head", sChoice); } else { if(DEBUG_LIST) DoDebug("Inserting into position between '" + sPrev + "' and '" + sNext + "'"); SetLocalString(oMagus, "PRC_EssentiaConvo_List_Next_" + sPrev, sChoice); } SetLocalString(oMagus, "PRC_EssentiaConvo_List_Next_" + sChoice, sNext); SetLocalInt(oMagus, "PRC_EssentiaConvo_List_" + sChoice, nChoice); } } /** * Reads the linked list built with AddToTempList() to AddChoice() and * deletes it. * * @param oMagus A PC gaining powers at the moment */ void TransferTempList(object oMagus) { string sChoice = GetLocalString(oMagus, "PRC_EssentiaConvo_List_Head"); int nChoice = GetLocalInt (oMagus, "PRC_EssentiaConvo_List_" + sChoice); DeleteLocalString(oMagus, "PRC_EssentiaConvo_List_Head"); string sPrev; if(DEBUG_LIST) DoDebug("Head is: '" + sChoice + "' - " + IntToString(nChoice)); while(sChoice != "") { // Add the choice AddChoice(sChoice, nChoice, oMagus); // Get next sChoice = GetLocalString(oMagus, "PRC_EssentiaConvo_List_Next_" + (sPrev = sChoice)); nChoice = GetLocalInt (oMagus, "PRC_EssentiaConvo_List_" + sChoice); if(DEBUG_LIST) DoDebug("Next is: '" + sChoice + "' - " + IntToString(nChoice) + "; previous = '" + sPrev + "'"); // Delete the already handled data DeleteLocalString(oMagus, "PRC_EssentiaConvo_List_Next_" + sPrev); DeleteLocalInt (oMagus, "PRC_EssentiaConvo_List_" + sPrev); } DeleteLocalInt(oMagus, "PRC_EssentiaConvo_ListInited"); } void LearnSpecificSpell(int nClass, int nSpellLevel, int nSpellbookID, object oPC) { // get location of persistant storage on the hide string sSpellbook = GetSpellsKnown_Array(nClass, nSpellLevel); //object oToken = GetHideToken(oPC); // Create spells known persistant array if it is missing int nSize = persistant_array_get_size(oPC, sSpellbook); if (nSize < 0) { persistant_array_create(oPC, sSpellbook); nSize = 0; } // Mark the spell as known (e.g. add it to the end of oPCs spellbook) persistant_array_set_int(oPC, sSpellbook, nSize, nSpellbookID); // increment the spells known value in the cache string sCache = "SKCCCache" + IntToString(nSpellLevel); int nKnown = GetLocalInt(oPC, sCache); if (nKnown) SetLocalInt(oPC, sCache, ++nKnown); if(GetSpellbookTypeForClass(nClass) == SPELLBOOK_TYPE_SPONTANEOUS) { // get the associated feat and IPfeat IDs string sFile = GetNSBDefinitionFileName(nClass); int nIPFeatID = StringToInt(Get2DACache(sFile, "IPFeatID", nSpellbookID)); int nFeatID = StringToInt(Get2DACache(sFile, "FeatID", nSpellbookID)); // Add spell use feats (this also places the bonus feat on the hide, so we can check whether oPC knows this spell by testing for the featID on the hide) AddSpellUse(oPC, nSpellbookID, nClass, sFile, "NewSpellbookMem_" + IntToString(nClass), SPELLBOOK_TYPE_SPONTANEOUS, GetPCSkin(oPC), nFeatID, nIPFeatID); } } void main() { object oMagus = GetPCSpeaker(); int nValue = GetLocalInt(oMagus, DYNCONV_VARIABLE); int nStage = GetStage(oMagus); int nLevel = GetLevelByClass(CLASS_TYPE_ULTIMATE_MAGUS, oMagus); // Check which of the conversation scripts called the scripts if(nValue == 0) // All of them set the DynConv_Var to non-zero value, so something is wrong -> abort return; if(nValue == DYNCONV_SETUP_STAGE) { if(DEBUG) DoDebug("prc_um_eskcon: Running setup stage for stage " + IntToString(nStage)); // Check if this stage is marked as already set up // This stops list duplication when scrolling if(!GetIsStageSetUp(nStage, oMagus)) { if(DEBUG) DoDebug("prc_um_eskcon: Stage was not set up already"); if(nStage == STAGE_SELECT_SPELL_LEVEL) { if(DEBUG) DoDebug("prc_um_eskcon: Building spell level selection"); SetHeader("Choose the level of spell to add to your spontaneous spells known."); // Set the tokens. int nType = GetPrimaryArcaneClass(oMagus); int nMax = GetMaxSpellLevelForCasterLevel(nType, GetLevelByTypeArcane(oMagus)); int nCap = 1; // 2nd level spells learned is capped at 1st level if (nLevel >= 10) nCap = 5; else if (nLevel >= 8) nCap = 4; else if (nLevel >= 6) nCap = 3; else if (nLevel >= 4) nCap = 2; // Max spell level to learn if (nCap > nMax) nMax = nCap; int i; for(i = 0; i < nMax; i++){ AddChoice(GetStringByStrRef(LEVEL_STRREF_START - i), // The minus is correct, these are stored in inverse order in the TLK. Whoops i + 1 ); } // Set the next, previous and wait tokens to default values SetDefaultTokens(); // Set the convo quit text to "Abort" SetCustomToken(DYNCONV_TOKEN_EXIT, GetStringByStrRef(DYNCONV_STRREF_ABORT_CONVO)); } else if(nStage == STAGE_SELECT_SPELL) { if(DEBUG) DoDebug("prc_um_eskcon: Building spell selection"); SetHeader("Choose the spell you wish to learn. It will be added to your Sorcerer's spells known."); int nType = GetPrimaryArcaneClass(oMagus); if (nType != CLASS_TYPE_INVALID) { //int nMax = GetMaxSpellLevelForCasterLevel(nType, GetLevelByTypeArcane(oMagus)); string sPowerFile = GetFileForClass(nType); if (nType == CLASS_TYPE_WIZARD) sPowerFile = "cls_spell_sorc"; int nLevelToBrowse = GetLocalInt(oMagus, "UltimateMagusLevel"); int i, nSpellLevel; string sFeatID; for(i = 0; i < 550 ; i++) { nSpellLevel = StringToInt(Get2DACache(sPowerFile, "Level", i)); if(nSpellLevel < nLevelToBrowse){ continue; } //FloatingTextStringOnCreature(IntToString(nSpellLevel)+" "+IntToString(i), oMagus); //Due to the way the 2das are structured, we know that once //the level of a read evocation is greater than the maximum castable //it'll never be lower again. Therefore, we can skip reading the //evocations that wouldn't be shown anyway. if(nSpellLevel > nLevelToBrowse){ break; } //sFeatID = Get2DACache(sPowerFile, "FeatID", i); int nSpellId = StringToInt(Get2DACache(sPowerFile, "RealSpellID", i)); if(!PRCGetIsRealSpellKnownByClass(nSpellId, CLASS_TYPE_SORCERER, oMagus) && PRCGetHasSpell(nSpellId, oMagus)) // Must know the spell, but not know it as a sorcerer { // Need to use the row number as choice for the NSB system AddChoice(GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nSpellId))), i, oMagus); } } } // Set the next, previous and wait tokens to default values SetDefaultTokens(); // Set the convo quit text to "Abort" SetCustomToken(DYNCONV_TOKEN_EXIT, GetStringByStrRef(DYNCONV_STRREF_ABORT_CONVO)); } else if(nStage == STAGE_CONFIRM_SELECTION_SPELL) { if(DEBUG) DoDebug("prc_um_eskcon: Building selection confirmation"); // Build the confirmation query int nSpell = GetLocalInt(oMagus, "nSpell"); string sToken = "You have chosen to learn "+GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nSpell)))+"."+ "\n\n"; sToken += GetStringByStrRef(STRREF_SELECTED_HEADER2); // "Is this correct?" SetHeader(sToken); AddChoice(GetStringByStrRef(STRREF_YES), TRUE, oMagus); // "Yes" AddChoice(GetStringByStrRef(STRREF_NO), FALSE, oMagus); // "No" } } // Do token setup SetupTokens(); } else if(nValue == DYNCONV_EXITED) { if(DEBUG) DoDebug("prc_um_eskcon: Running exit handler"); // End of conversation cleanup DeleteLocalInt(oMagus, "nSpell"); DeleteLocalInt(oMagus, "UltimateMagusLevel"); } else if(nValue == DYNCONV_ABORTED) { // End of conversation cleanup DeleteLocalInt(oMagus, "nSpell"); DeleteLocalInt(oMagus, "UltimateMagusLevel"); } // Handle PC response else { int nChoice = GetChoice(oMagus); if(DEBUG) DoDebug("prc_um_eskcon: Handling PC response, stage = " + IntToString(nStage) + "; nChoice = " + IntToString(nChoice) + "; choice text = '" + GetChoiceText(oMagus) + "'"); if(nStage == STAGE_SELECT_SPELL_LEVEL) { if(DEBUG) DoDebug("prc_um_eskcon: spell level selected"); SetLocalInt(oMagus, "UltimateMagusLevel", nChoice); nStage = STAGE_SELECT_SPELL; MarkStageNotSetUp(STAGE_SELECT_SPELL_LEVEL, oMagus); } else if(nStage == STAGE_SELECT_SPELL) { if(DEBUG) DoDebug("prc_um_eskcon: Spell selected"); SetLocalInt(oMagus, "nSpell", nChoice); nStage = STAGE_CONFIRM_SELECTION_SPELL; MarkStageNotSetUp(STAGE_SELECT_SPELL, oMagus); } else if(nStage == STAGE_CONFIRM_SELECTION_SPELL) { if (nChoice) //Put add spells known here { LearnSpecificSpell(CLASS_TYPE_SORCERER, GetLocalInt(oMagus, "UltimateMagusLevel"), GetLocalInt(oMagus, "nSpell"), oMagus); DeleteLocalInt(oMagus, "nSpell"); DeleteLocalInt(oMagus, "UltimateMagusLevel"); AllowExit(DYNCONV_EXIT_FORCE_EXIT); } MarkStageNotSetUp(STAGE_CONFIRM_SELECTION_SPELL, oMagus); } if(DEBUG) DoDebug("prc_um_eskcon: New stage: " + IntToString(nStage)); // Store the stage value. If it has been changed, this clears out the choices SetStage(nStage, oMagus); } }