From 1b72d22b122f42a6c712b39fe5220f8122028e36 Mon Sep 17 00:00:00 2001 From: Jaysyn904 <68194417+Jaysyn904@users.noreply.github.com> Date: Sat, 7 Mar 2026 09:41:17 -0500 Subject: [PATCH] 2026/03/07 Update Further work on the Background system. --- src/nss/bg_language_cv.nss | 867 +++++++++++++++++++++++++++++++++---- src/nss/bg_profs_cv.nss | 534 +++++++++++++++++------ src/nss/clear_bg_vars.nss | 79 ++++ 3 files changed, 1256 insertions(+), 224 deletions(-) create mode 100644 src/nss/clear_bg_vars.nss diff --git a/src/nss/bg_language_cv.nss b/src/nss/bg_language_cv.nss index 298ecf5..ccbb2b7 100644 --- a/src/nss/bg_language_cv.nss +++ b/src/nss/bg_language_cv.nss @@ -7,10 +7,11 @@ const int STAGE_LIST = 0; const int STAGE_CONFIRM = 1; - // Ensure the PC Data Object exists; create if missing object EnsurePlayerDataObject(object oPC) -{ +{ + SendMessageToPC(oPC, "Language data object recreated"); + WriteTimestampedLogEntry("Language data object recreated"); object oItem = GetItemPossessedBy(oPC, "PC_Data_Object"); if (!GetIsObjectValid(oItem)) { @@ -18,30 +19,47 @@ object EnsurePlayerDataObject(object oPC) } return oItem; } - -// Check if PC already knows a language -int KnowsLanguage(object oPC, int nLanguageFeat) -{ - object oItem = EnsurePlayerDataObject(oPC); - - // Search through all LANGUAGE_* slots for this feat - int i = 0; - string sSlot; - while (i < 99) - { - sSlot = "LANGUAGE_" + (i < 20 ? "0" : "") + IntToString(i); - if (GetLocalInt(oItem, sSlot) == nLanguageFeat) - return TRUE; - i++; - } - return FALSE; + +// Check if PC already knows a language +int KnowsLanguage(object oPC, int nLanguageFeat) +{ + object oItem = EnsurePlayerDataObject(oPC); + + int i = 0; + string sSlot; + + while (i < 20) + { + sSlot = "LANGUAGE_" + (i < 10 ? "0" : "") + IntToString(i); + + if (GetPersistantLocalInt(oPC, sSlot) == nLanguageFeat) + return TRUE; + + if (GetIsObjectValid(oItem)) + { + if (GetLocalInt(oItem, sSlot) == nLanguageFeat) + return TRUE; + } + + i++; + } + + return FALSE; +} + +void AddChoiceIfNotKnown(string sText, int nValue, object oPC, int nLanguageFeat) +{ + if (!GetLocalInt(oPC, IntToString(nLanguageFeat)) && + !KnowsLanguage(oPC, nLanguageFeat)) + { + AddChoice(sText, nValue, oPC); + } } - // Function to get the next available language slot int GetNextLanguageSlot(object oPC) { - object oItem = GetItemPossessedBy(oPC, "PC_Data_Object"); + object oItem = EnsurePlayerDataObject(oPC); if (!GetIsObjectValid(oItem)) return 0; int i = 0; @@ -50,17 +68,63 @@ int GetNextLanguageSlot(object oPC) // Find the first empty slot while (i < 99) // Maximum 99 languages { - sSlot = "LANGUAGE_" + (i < 20 ? "0" : "") + IntToString(i); + sSlot = "LANGUAGE_" + (i < 10 ? "0" : "") + IntToString(i); if (!GetLocalInt(oItem, sSlot)) return i; i++; } return -1; // No slots available } + +void GrantLanguage(object oPC, int nLanguageFeat) +{ + object oItem = EnsurePlayerDataObject(oPC); + if (!GetIsObjectValid(oItem)) return; + + // Debug: Check if language already exists + if (KnowsLanguage(oPC, nLanguageFeat)) { + string sMsg = "Language " + IntToString(nLanguageFeat) + " already exists, skipping grant"; + SendMessageToPC(oPC, sMsg); + WriteTimestampedLogEntry(sMsg); + return; + } + + string sMsg = "Granting language " + IntToString(nLanguageFeat); + SendMessageToPC(oPC, sMsg); + WriteTimestampedLogEntry(sMsg); + + int nSlot = GetNextLanguageSlot(oPC); + if (nSlot >= 0) + { + string sSlot = "LANGUAGE_" + (nSlot < 20 ? "0" : "") + IntToString(nSlot); + SetLocalInt(oItem, sSlot, nLanguageFeat); + SetPersistantLocalInt(oPC, sSlot, nLanguageFeat); + + sMsg = "Stored in slot " + sSlot; + SendMessageToPC(oPC, sMsg); + WriteTimestampedLogEntry(sMsg); + } +} - -// Grant a language to PC -void GrantLanguage(object oPC, int nLanguageFeat) +/* // Grant a language to PC +void GrantLanguage(object oPC, int nLanguageFeat) +{ + object oItem = GetItemPossessedBy(oPC, "PC_Data_Object"); + if (!GetIsObjectValid(oItem)) return; + + // Check if language already exists + if (KnowsLanguage(oPC, nLanguageFeat)) return; + + int nSlot = GetNextLanguageSlot(oPC); + if (nSlot >= 0) + { + string sSlot = "LANGUAGE_" + (nSlot < 20 ? "0" : "") + IntToString(nSlot); + SetLocalInt(oItem, sSlot, nLanguageFeat); + SetPersistantLocalInt(oPC, sSlot, nLanguageFeat); + } +} */ + +/* void GrantLanguage(object oPC, int nLanguageFeat) { object oItem = GetItemPossessedBy(oPC, "PC_Data_Object"); if (!GetIsObjectValid(oItem)) return; @@ -73,64 +137,117 @@ void GrantLanguage(object oPC, int nLanguageFeat) SetPersistantLocalInt(oPC, sSlot, nLanguageFeat); } } + */ -// Count automatic racial and class languages using slot-searching pattern -int GetAutomaticLanguageCount(object oPC) -{ - int nAutomaticLanguages = 0; - int nRace = GetRacialType(oPC); - object oItem = GetItemPossessedBy(oPC, "PC_Data_Object"); - if (!GetIsObjectValid(oItem)) return 0; - - // Search through all language slots - int i = 0; - string sSlot; - int nLanguageFeat; - - while (i < 99) - { - sSlot = "LANGUAGE_" + (i < 10 ? "0" : "") + IntToString(i); - nLanguageFeat = GetLocalInt(oItem, sSlot); - - if (nLanguageFeat > 0) - { - // Check if this is an automatic racial language - switch(nRace) - { - case RACIAL_TYPE_ELF: case RACIAL_TYPE_HALFELF: - if (nLanguageFeat == FEAT_LANGUAGE_ELVEN) nAutomaticLanguages++; - break; - case RACIAL_TYPE_DWARF: - if (nLanguageFeat == FEAT_LANGUAGE_DWARVEN) nAutomaticLanguages++; - break; - case RACIAL_TYPE_GNOME: - if (nLanguageFeat == FEAT_LANGUAGE_GNOMISH) nAutomaticLanguages++; - break; - case RACIAL_TYPE_HALFLING: - if (nLanguageFeat == FEAT_LANGUAGE_HALFLING) nAutomaticLanguages++; - break; - case RACIAL_TYPE_HALFORC: - if (nLanguageFeat == FEAT_LANGUAGE_ORCISH) nAutomaticLanguages++; - break; - } - - // Check if this is an automatic class language - if (GetLevelByClass(CLASS_TYPE_ROGUE, oPC) >= 1 && nLanguageFeat == FEAT_LANGUAGE_THIEVES_CANT) - nAutomaticLanguages++; - if (GetLevelByClass(CLASS_TYPE_DRUID, oPC) >= 5 && nLanguageFeat == FEAT_LANGUAGE_DRUIDIC) - nAutomaticLanguages++; - if ((GetLevelByClass(CLASS_TYPE_RANGER, oPC) >= 8 || GetLevelByClass(CLASS_TYPE_DRUID, oPC) >= 5) && nLanguageFeat == FEAT_LANGUAGE_ANIMAL) - nAutomaticLanguages++; - if (GetLevelByClass(CLASS_TYPE_ASSASSIN, oPC) >= 1 && nLanguageFeat == FEAT_LANGUAGE_ASSASSINS_CANT) - nAutomaticLanguages++; - } - - i++; - } - - return nAutomaticLanguages; -} - +// Count automatic racial and class languages using slot-searching pattern +int GetAutomaticLanguageCount(object oPC) +{ + int nAutomaticLanguages = 0; + int nRace = GetRacialType(oPC); + object oItem = EnsurePlayerDataObject(oPC); + if (!GetIsObjectValid(oItem)) return 0; + + // Get saved character creation choices + int nSubrace = GetLocalInt(oItem, "CC0"); + int nBackground = GetLocalInt(oItem, "CC2"); + + // Search through all language slots + int i = 0; + string sSlot; + int nLanguageFeat; + + while (i < 20) + { + sSlot = "LANGUAGE_" + (i < 10 ? "0" : "") + IntToString(i); + nLanguageFeat = GetPersistantLocalInt(oPC, sSlot); + + if (nLanguageFeat > 0) + { + // Check if this is an automatic racial language + switch(nRace) + { + case RACIAL_TYPE_ELF: case RACIAL_TYPE_HALFELF: + if (nLanguageFeat == FEAT_LANGUAGE_ELVEN) nAutomaticLanguages++; + break; + case RACIAL_TYPE_DWARF: + if (nLanguageFeat == FEAT_LANGUAGE_DWARVEN) nAutomaticLanguages++; + break; + case RACIAL_TYPE_GNOME: + if (nLanguageFeat == FEAT_LANGUAGE_GNOMISH) nAutomaticLanguages++; + break; + case RACIAL_TYPE_HALFLING: + if (nLanguageFeat == FEAT_LANGUAGE_HALFLING) nAutomaticLanguages++; + break; + case RACIAL_TYPE_HALFORC: + if (nLanguageFeat == FEAT_LANGUAGE_ORCISH) nAutomaticLanguages++; + break; + } + + // Check if this is an automatic class language + int nRanger = GetLevelByClass(CLASS_TYPE_RANGER, oPC); + int nDruid = GetLevelByClass(CLASS_TYPE_DRUID, oPC); + int nRogue = GetLevelByClass(CLASS_TYPE_ROGUE, oPC); + int nAssn = GetLevelByClass(CLASS_TYPE_ASSASSIN, oPC); + + if ((nRanger >= 8 || nDruid >= 5 || nBackground == BACKGROUND_CIRCLE_BORN) && nLanguageFeat == FEAT_LANGUAGE_ANIMAL) + nAutomaticLanguages++; + if (nDruid >= 5 && nLanguageFeat == FEAT_LANGUAGE_DRUIDIC) + nAutomaticLanguages++; + if ((nRogue > 0 || nAssn > 0) && nLanguageFeat == FEAT_LANGUAGE_THIEVES_CANT) + nAutomaticLanguages++; + if (nAssn > 0 && nLanguageFeat == FEAT_LANGUAGE_ASSASSINS_CANT) + nAutomaticLanguages++; + + // Check if this is an automatic ethnicity language + if ((nSubrace == ETHNICITY_CALISHITE || nBackground == BACKGROUND_CALISHITE_TRAINED) && nLanguageFeat == FEAT_LANGUAGE_ALZHEDO) + nAutomaticLanguages++; + if (nSubrace == ETHNICITY_CHONDATHAN && nLanguageFeat == FEAT_LANGUAGE_CHONDATHAN) + nAutomaticLanguages++; + if (nSubrace == ETHNICITY_DAMARAN && nLanguageFeat == FEAT_LANGUAGE_DAMARAN) + nAutomaticLanguages++; + if (nSubrace == ETHNICITY_ILLUSKAN && nLanguageFeat == FEAT_LANGUAGE_ILLUSKAN) + nAutomaticLanguages++; + if (nSubrace == ETHNICITY_MULAN && nLanguageFeat == FEAT_LANGUAGE_MULANESE) + nAutomaticLanguages++; + if (nSubrace == ETHNICITY_RASHEMI && nLanguageFeat == FEAT_LANGUAGE_RASHEMI) + nAutomaticLanguages++; + if (nSubrace == ETHNICITY_CHULTAN && nLanguageFeat == FEAT_LANGUAGE_CHULTAN) + nAutomaticLanguages++; + if (nSubrace == ETHNICITY_TETHYRIAN && nLanguageFeat == FEAT_LANGUAGE_ALZHEDO) + nAutomaticLanguages++; + if (nSubrace == ETHNICITY_FFOLK && nLanguageFeat == FEAT_LANGUAGE_TALFIRIC) + nAutomaticLanguages++; + if (nSubrace == ETHNICITY_SHAARAN && nLanguageFeat == FEAT_LANGUAGE_SHAARAN) + nAutomaticLanguages++; + if (nSubrace == ETHNICITY_IMASKARI && nLanguageFeat == FEAT_LANGUAGE_IMASKARI) + nAutomaticLanguages++; + + // Check if this is an automatic special subrace language + if (nBackground == BACKGROUND_DARK_ELF) + { + if (nLanguageFeat == FEAT_LANGUAGE_DROW) nAutomaticLanguages++; + if (nLanguageFeat == FEAT_LANGUAGE_DROW_HAND_CANT) nAutomaticLanguages++; + if (nLanguageFeat == FEAT_LANGUAGE_UNDERCOMMON) nAutomaticLanguages++; + } + if (nBackground == BACKGROUND_GREY_DWARF) + { + if (nLanguageFeat == FEAT_LANGUAGE_DUERGAR) nAutomaticLanguages++; + if (nLanguageFeat == FEAT_LANGUAGE_UNDERCOMMON) nAutomaticLanguages++; + } + + // Check if this is an automatic planar language + if (nBackground == 1137 && nLanguageFeat == FEAT_LANGUAGE_ABYSSAL) // Abyssal_Pact + nAutomaticLanguages++; + if (nBackground == 1139 && nLanguageFeat == FEAT_LANGUAGE_INFERNAL) // Dark_Pact + nAutomaticLanguages++; + } + + i++; + } + + return nAutomaticLanguages; +} + // Calculate remaining bonus language picks int GetBonusLanguageCount(object oPC) { @@ -149,7 +266,7 @@ int GetBonusLanguageCount(object oPC) string sSlot; while (i < 99) { - sSlot = "LANGUAGE_" + (i < 20 ? "0" : "") + IntToString(i); + sSlot = "LANGUAGE_" + (i < 10 ? "0" : "") + IntToString(i); if (GetLocalInt(oItem, sSlot)) nSelected++; i++; @@ -166,7 +283,7 @@ int GetBonusLanguageCount(object oPC) void GrantDefaultLanguages(object oPC) { - object oItem = GetItemPossessedBy(oPC, "PC_Data_Object"); + object oItem = EnsurePlayerDataObject(oPC); if (!GetIsObjectValid(oItem)) return; // Get saved character creation choices @@ -214,7 +331,7 @@ void GrantDefaultLanguages(object oPC) // Ethnicity Languages if (nSubrace == ETHNICITY_CALISHITE || nBackground == BACKGROUND_CALISHITE_TRAINED) GrantLanguage(oPC, FEAT_LANGUAGE_ALZHEDO); - if (nSubrace == ETHNICITY_CHONDATHAN || nSubrace == ETHNICITY_TETHYRIAN) + if (nSubrace == ETHNICITY_CHONDATHAN) GrantLanguage(oPC, FEAT_LANGUAGE_CHONDATHAN); if (nSubrace == ETHNICITY_DAMARAN) GrantLanguage(oPC, FEAT_LANGUAGE_DAMARAN); @@ -223,7 +340,17 @@ void GrantDefaultLanguages(object oPC) if (nSubrace == ETHNICITY_MULAN) GrantLanguage(oPC, FEAT_LANGUAGE_MULANESE); if (nSubrace == ETHNICITY_RASHEMI) - GrantLanguage(oPC, FEAT_LANGUAGE_RASHEMI); + GrantLanguage(oPC, FEAT_LANGUAGE_RASHEMI); + if (nSubrace == ETHNICITY_CHULTAN) + GrantLanguage(oPC, FEAT_LANGUAGE_CHULTAN); + if (nSubrace == ETHNICITY_TETHYRIAN) + GrantLanguage(oPC, FEAT_LANGUAGE_ALZHEDO); + if (nSubrace == ETHNICITY_FFOLK) + GrantLanguage(oPC, FEAT_LANGUAGE_TALFIRIC); + if (nSubrace == ETHNICITY_SHAARAN) + GrantLanguage(oPC, FEAT_LANGUAGE_SHAARAN); + if (nSubrace == ETHNICITY_IMASKARI) + GrantLanguage(oPC, FEAT_LANGUAGE_IMASKARI); // Special Subrace Languages if (nBackground == BACKGROUND_DARK_ELF) @@ -246,7 +373,571 @@ void GrantDefaultLanguages(object oPC) GrantLanguage(oPC, FEAT_LANGUAGE_INFERNAL); } -void main() + + void main() +{ + object oPC = GetPCSpeaker(); + object oItem = EnsurePlayerDataObject(oPC); + + GrantDefaultLanguages(oPC); + + int nTotal = 0; + int i = 0; + string sSlot; + while (i < 20) { + sSlot = "LANGUAGE_" + (i < 10 ? "0" : "") + IntToString(i); + if (GetLocalInt(oItem, sSlot)) { + nTotal++; + SendMessageToPC(oPC, "***************LANGUAGE*TEST********************"); + WriteTimestampedLogEntry("***************LANGUAGE*TEST********************"); + SendMessageToPC(oPC, "Found language in slot " + sSlot + ": " + IntToString(GetLocalInt(oItem, sSlot))); + WriteTimestampedLogEntry("Found language in slot " + sSlot + ": " + IntToString(GetLocalInt(oItem, sSlot))); + } + i++; + } + + SendMessageToPC(oPC, "Total languages: " + IntToString(nTotal)); + WriteTimestampedLogEntry("Total languages: " + IntToString(nTotal)); + SendMessageToPC(oPC, "Automatic count: " + IntToString(GetAutomaticLanguageCount(oPC))); + WriteTimestampedLogEntry("Automatic count: " + IntToString(GetAutomaticLanguageCount(oPC))); + SendMessageToPC(oPC, "Bonus remaining: " + IntToString(GetBonusLanguageCount(oPC))); + WriteTimestampedLogEntry("Bonus remaining: " + IntToString(GetBonusLanguageCount(oPC))); + + int nValue = GetLocalInt(oPC, DYNCONV_VARIABLE); + int nStage = GetStage(oPC); + int nChoice = GetChoice(); + + int nBackground = GetLocalInt(oItem, "CC2"); + + int nCleric = GetLevelByClass(CLASS_TYPE_CLERIC, oPC); + int nDruid = GetLevelByClass(CLASS_TYPE_DRUID, oPC); + int nWizard = GetLevelByClass(CLASS_TYPE_WIZARD, oPC); + int nDragDisc = GetLevelByClass(CLASS_TYPE_DRAGON_DISCIPLE, oPC); + + int nAir = GetHasFeat(FEAT_AIR_DOMAIN_POWER, oPC); + int nEarth = GetHasFeat(FEAT_EARTH_DOMAIN_POWER, oPC); + int nFire = GetHasFeat(FEAT_FIRE_DOMAIN_POWER, oPC); + int nWater = GetHasFeat(FEAT_WATER_DOMAIN_POWER, oPC); + + if(nValue == 0) return; + + if(nValue == DYNCONV_SETUP_STAGE) + { + if(!GetIsStageSetUp(nStage, oPC)) + { + if(nStage == STAGE_LIST) + { + int nRemaining = GetBonusLanguageCount(oPC); + string sHeader = "You have " + IntToString(nRemaining) + " bonus language picks remaining.\n\ You can refresh the list with the Escape if needed.\n\nSelect a language:"; + SetHeader(sHeader); + + // Add all available languages that PC doesn't know and has picks for + if (nRemaining > 0 && nCleric) { + AddChoiceIfNotKnown("Abyssal", 1, oPC, FEAT_LANGUAGE_ABYSSAL); + } + if (nRemaining > 0) { + AddChoiceIfNotKnown("Alzhedo", 2, oPC, FEAT_LANGUAGE_ALZHEDO); + } + if (nRemaining > 0 && nWater) { + AddChoiceIfNotKnown("Aquan", 3, oPC, FEAT_LANGUAGE_AQUAN); + } + if (nRemaining > 0 && nAir) { + AddChoiceIfNotKnown("Auran", 5, oPC, FEAT_LANGUAGE_AURAN); + } + if (nRemaining > 0 && nCleric) { + AddChoiceIfNotKnown("Celestial", 6, oPC, FEAT_LANGUAGE_CELESTIAL); + } + if (nRemaining > 0) { + AddChoiceIfNotKnown("Chessentan", 7, oPC, FEAT_LANGUAGE_CHESSENTAN); + } + if (nRemaining > 0) { + AddChoiceIfNotKnown("Chondathan", 8, oPC, FEAT_LANGUAGE_CHONDATHAN); + } + if (nRemaining > 0) { + AddChoiceIfNotKnown("Chultan", 9, oPC, FEAT_LANGUAGE_CHULTAN); + } + if (nRemaining > 0) { + AddChoiceIfNotKnown("Damaran", 10, oPC, FEAT_LANGUAGE_DAMARAN); + } + if (nRemaining > 0 && (nWizard || nDragDisc)) { + AddChoiceIfNotKnown("Draconic", 11, oPC, FEAT_LANGUAGE_DRACONIC); + } + if (nRemaining > 0 && (KnowsLanguage(oPC, FEAT_LANGUAGE_UNDERCOMMON) || + KnowsLanguage(oPC, FEAT_LANGUAGE_ELVEN) || + KnowsLanguage(oPC, FEAT_LANGUAGE_IMASKARI))) { + AddChoiceIfNotKnown("Drow", 12, oPC, FEAT_LANGUAGE_DROW); + } + if (nRemaining > 0 && (KnowsLanguage(oPC, FEAT_LANGUAGE_UNDERCOMMON) || + KnowsLanguage(oPC, FEAT_LANGUAGE_DWARVEN) || + KnowsLanguage(oPC, FEAT_LANGUAGE_IMASKARI))) { + AddChoiceIfNotKnown("Duergar", 13, oPC, FEAT_LANGUAGE_DUERGAR); + } + if (nRemaining > 0) { + AddChoiceIfNotKnown("Dwarven", 14, oPC, FEAT_LANGUAGE_DWARVEN); + } + if (nRemaining > 0) { + AddChoiceIfNotKnown("Elven", 15, oPC, FEAT_LANGUAGE_ELVEN); + } + if (nRemaining > 0) { + AddChoiceIfNotKnown("Giant", 16, oPC, FEAT_LANGUAGE_GIANT); + } + if (nRemaining > 0) { + AddChoiceIfNotKnown("Gnomish", 17, oPC, FEAT_LANGUAGE_GNOMISH); + } + if (nRemaining > 0) { + AddChoiceIfNotKnown("Goblin", 18, oPC, FEAT_LANGUAGE_GOBLIN); + } + if (nRemaining > 0) { + AddChoiceIfNotKnown("Halfling", 19, oPC, FEAT_LANGUAGE_HALFLING); + } + if (nRemaining > 0 && nFire) { + AddChoiceIfNotKnown("Ignan", 20, oPC, FEAT_LANGUAGE_IGNAN); + } + if (nRemaining > 0) { + AddChoiceIfNotKnown("Illuskan", 21, oPC, FEAT_LANGUAGE_ILLUSKAN); + } + if (nRemaining > 0) { + AddChoiceIfNotKnown("Imaskari", 22, oPC, FEAT_LANGUAGE_IMASKARI); + } + if (nRemaining > 0 && nCleric) { + AddChoiceIfNotKnown("Infernal", 23, oPC, FEAT_LANGUAGE_INFERNAL); + } + if (nRemaining > 0) { + AddChoiceIfNotKnown("Lantanese", 24, oPC, FEAT_LANGUAGE_LANTANESE); + } + if (nRemaining > 0) { + AddChoiceIfNotKnown("Mulanese", 25, oPC, FEAT_LANGUAGE_MAZTILAN); + } + if (nRemaining > 0) { + AddChoiceIfNotKnown("Mulanese", 26, oPC, FEAT_LANGUAGE_MULANESE); + } + if (nRemaining > 0) { + AddChoiceIfNotKnown("Mulhorandi", 27, oPC, FEAT_LANGUAGE_MULHORANDI); + } + if (nRemaining > 0) { + AddChoiceIfNotKnown("Orcish", 28, oPC, FEAT_LANGUAGE_ORCISH); + } + if (nRemaining > 0) { + AddChoiceIfNotKnown("Rashemi", 29, oPC, FEAT_LANGUAGE_RASHEMI); + } + if (nRemaining > 0 && nDruid) { + AddChoiceIfNotKnown("Sylvan", 30, oPC, FEAT_LANGUAGE_SYLVAN); + } + if (nRemaining > 0) { + AddChoiceIfNotKnown("Talfiric", 31, oPC, FEAT_LANGUAGE_TALFIRIC); + } + if (nRemaining > 0 && nEarth) { + AddChoiceIfNotKnown("Terran", 32, oPC, FEAT_LANGUAGE_TERRAN); + } + if (nRemaining > 0) { + AddChoiceIfNotKnown("Troglodyte", 33, oPC, FEAT_LANGUAGE_TROGLODYTE); + } + if (nRemaining > 0) { + AddChoiceIfNotKnown("Undercommon", 34, oPC, FEAT_LANGUAGE_UNDERCOMMON); + } + + + /* if (nRemaining > 0 && nCleric && !KnowsLanguage(oPC, FEAT_LANGUAGE_ABYSSAL)) + { + AddChoice("Abyssal", 1, oPC); + } + if (nRemaining > 0 && !KnowsLanguage(oPC, FEAT_LANGUAGE_ALZHEDO)) + { + AddChoice("Alzhedo", 2, oPC); + } + if (nRemaining > 0 && nWater && !KnowsLanguage(oPC, FEAT_LANGUAGE_AQUAN)) + { + AddChoice("Aquan", 3, oPC); + } + if (nRemaining > 0 && nAir && !KnowsLanguage(oPC, FEAT_LANGUAGE_AURAN)) + { + AddChoice("Auran", 5, oPC); + } + if (nRemaining > 0 && nCleric && !KnowsLanguage(oPC, FEAT_LANGUAGE_CELESTIAL)) + { + AddChoice("Celestial", 6, oPC); + } + if (nRemaining > 0 && !KnowsLanguage(oPC, FEAT_LANGUAGE_CHESSENTAN)) + { + AddChoice("Chessentan", 7, oPC); + } + if (nRemaining > 0 && !KnowsLanguage(oPC, FEAT_LANGUAGE_CHONDATHAN)) + { + AddChoice("Chondathan", 8, oPC); + } + if (nRemaining > 0 && !KnowsLanguage(oPC, FEAT_LANGUAGE_CHULTAN)) + { + AddChoice("Chultan", 9, oPC); + } + if (nRemaining > 0 && !KnowsLanguage(oPC, FEAT_LANGUAGE_DAMARAN)) + { + AddChoice("Damaran", 10, oPC); + } + if (nRemaining > 0 && ( nWizard || nDragDisc) && !KnowsLanguage(oPC, FEAT_LANGUAGE_DRACONIC)) + { + AddChoice("Draconic", 11, oPC); + } + if (nRemaining > 0 && (KnowsLanguage(oPC, FEAT_LANGUAGE_UNDERCOMMON) || + KnowsLanguage(oPC, FEAT_LANGUAGE_ELVEN) || + KnowsLanguage(oPC, FEAT_LANGUAGE_IMASKARI)) && + !KnowsLanguage(oPC, FEAT_LANGUAGE_DROW)) + { + AddChoice("Drow", 12, oPC); + } + if (nRemaining > 0 && (KnowsLanguage(oPC, FEAT_LANGUAGE_UNDERCOMMON) || + KnowsLanguage(oPC, FEAT_LANGUAGE_DWARVEN) || + KnowsLanguage(oPC, FEAT_LANGUAGE_IMASKARI)) && + !KnowsLanguage(oPC, FEAT_LANGUAGE_DUERGAR)) + { + AddChoice("Duergar", 13, oPC); + } + if (nRemaining > 0 && !KnowsLanguage(oPC, FEAT_LANGUAGE_DWARVEN)) + { + AddChoice("Dwarven", 14, oPC); + } + if (nRemaining > 0 && !KnowsLanguage(oPC, FEAT_LANGUAGE_ELVEN)) + { + AddChoice("Elven", 15, oPC); + } + if (nRemaining > 0 && !KnowsLanguage(oPC, FEAT_LANGUAGE_GIANT)) + { + AddChoice("Giant", 16, oPC); + } + if (nRemaining > 0 && !KnowsLanguage(oPC, FEAT_LANGUAGE_GNOMISH)) + { + AddChoice("Gnomish", 17, oPC); + } + if (nRemaining > 0 && !KnowsLanguage(oPC, FEAT_LANGUAGE_GOBLIN)) + { + AddChoice("Goblin", 18, oPC); + } + if (nRemaining > 0 && !KnowsLanguage(oPC, FEAT_LANGUAGE_HALFLING)) + { + AddChoice("Halfling", 19, oPC); + } + if (nRemaining > 0 && nFire && !KnowsLanguage(oPC, FEAT_LANGUAGE_IGNAN)) + { + AddChoice("Ignan", 20, oPC); + } + if (nRemaining > 0 && !KnowsLanguage(oPC, FEAT_LANGUAGE_ILLUSKAN)) + { + AddChoice("Illuskan", 21, oPC); + } + if (nRemaining > 0 && !KnowsLanguage(oPC, FEAT_LANGUAGE_IMASKARI)) + { + AddChoice("Imaskari", 22, oPC); + } + if (nRemaining > 0 && nCleric && !KnowsLanguage(oPC, FEAT_LANGUAGE_INFERNAL)) + { + AddChoice("Infernal", 23, oPC); + } + if (nRemaining > 0 && !KnowsLanguage(oPC, FEAT_LANGUAGE_LANTANESE)) + { + AddChoice("Lantanese", 24, oPC); + } + if (nRemaining > 0 && !KnowsLanguage(oPC, FEAT_LANGUAGE_MAZTILAN)) + { + AddChoice("Mulanese", 25, oPC); + } + if (nRemaining > 0 && !KnowsLanguage(oPC, FEAT_LANGUAGE_MULANESE)) + { + AddChoice("Mulanese", 26, oPC); + } + if (nRemaining > 0 && !KnowsLanguage(oPC, FEAT_LANGUAGE_MULHORANDI)) + { + AddChoice("Mulhorandi", 27, oPC); + } + if (nRemaining > 0 && !KnowsLanguage(oPC, FEAT_LANGUAGE_ORCISH)) + { + AddChoice("Orcish", 28, oPC); + } + if (nRemaining > 0 && !KnowsLanguage(oPC, FEAT_LANGUAGE_RASHEMI)) + { + AddChoice("Rashemi", 29, oPC); + } + if (nRemaining > 0 && nDruid && !KnowsLanguage(oPC, FEAT_LANGUAGE_SYLVAN)) + { + AddChoice("Sylvan", 30, oPC); + } + if (nRemaining > 0 && !KnowsLanguage(oPC, FEAT_LANGUAGE_TALFIRIC)) + { + AddChoice("Talfiric", 31, oPC); + } + if (nRemaining > 0 && nEarth && !KnowsLanguage(oPC, FEAT_LANGUAGE_TERRAN)) + { + AddChoice("Terran", 32, oPC); + } + if (nRemaining > 0 && !KnowsLanguage(oPC, FEAT_LANGUAGE_TROGLODYTE)) + { + AddChoice("Troglodyte", 33, oPC); + } + if (nRemaining > 0 && !KnowsLanguage(oPC, FEAT_LANGUAGE_UNDERCOMMON)) + { + AddChoice("Undercommon", 34, oPC); + } */ + + // Refresh option + AddChoice("[Refresh Language Choices]", 99, oPC); + + MarkStageSetUp(nStage, oPC); + SetDefaultTokens(); + } + } + SetupTokens(); + } + // Add exit handler to start next conversation + else if(nValue == DYNCONV_EXITED) + { + DelayCommand(0.1f, StartDynamicConversation("bg_proficiency_cv", oPC)); + } + // Handle PC responses + else + { + if(nStage == STAGE_LIST) + { + // Handle language selection + if (nChoice >= 1 && nChoice <= 34) + { + int nRemaining = GetBonusLanguageCount(oPC); + + if (nRemaining > 0) + { + // Grant the selected language based on choice + switch (nChoice) + { + case 1: + { + GrantLanguage(oPC, FEAT_LANGUAGE_ABYSSAL); + SetLocalInt(oPC, IntToString(FEAT_LANGUAGE_ABYSSAL), TRUE); + break; + } + case 2: + { + GrantLanguage(oPC, FEAT_LANGUAGE_ALZHEDO); + SetLocalInt(oPC, IntToString(FEAT_LANGUAGE_ALZHEDO), TRUE); + break; + } + case 3: + { + GrantLanguage(oPC, FEAT_LANGUAGE_AQUAN); + SetLocalInt(oPC, IntToString(FEAT_LANGUAGE_AQUAN), TRUE); + break; + } + case 4: + { + GrantLanguage(oPC, FEAT_LANGUAGE_AURAN); + SetLocalInt(oPC, IntToString(FEAT_LANGUAGE_AURAN), TRUE); + break; + } + case 6: + { + GrantLanguage(oPC, FEAT_LANGUAGE_CELESTIAL); + SetLocalInt(oPC, IntToString(FEAT_LANGUAGE_CELESTIAL), TRUE); + break; + } + case 7: + { + GrantLanguage(oPC, FEAT_LANGUAGE_CHESSENTAN); + SetLocalInt(oPC, IntToString(FEAT_LANGUAGE_CHESSENTAN), TRUE); + break; + } + case 8: + { + GrantLanguage(oPC, FEAT_LANGUAGE_CHONDATHAN); + SetLocalInt(oPC, IntToString(FEAT_LANGUAGE_CHONDATHAN), TRUE); + break; + } + case 9: + { + GrantLanguage(oPC, FEAT_LANGUAGE_CHULTAN); + SetLocalInt(oPC, IntToString(FEAT_LANGUAGE_CHULTAN), TRUE); + break; + } + case 10: + { + GrantLanguage(oPC, FEAT_LANGUAGE_DAMARAN); + SetLocalInt(oPC, IntToString(FEAT_LANGUAGE_DAMARAN), TRUE); + break; + } + case 11: + { + GrantLanguage(oPC, FEAT_LANGUAGE_DRACONIC); + SetLocalInt(oPC, IntToString(FEAT_LANGUAGE_DRACONIC), TRUE); + break; + } + case 12: + { + GrantLanguage(oPC, FEAT_LANGUAGE_DROW); + SetLocalInt(oPC, IntToString(FEAT_LANGUAGE_DROW), TRUE); + break; + } + case 13: + { + GrantLanguage(oPC, FEAT_LANGUAGE_DUERGAR); + SetLocalInt(oPC, IntToString(FEAT_LANGUAGE_DUERGAR), TRUE); + break; + } + case 14: + { + GrantLanguage(oPC, FEAT_LANGUAGE_DWARVEN); + SetLocalInt(oPC, IntToString(FEAT_LANGUAGE_DWARVEN), TRUE); + break; + } + case 15: + { + GrantLanguage(oPC, FEAT_LANGUAGE_ELVEN); + SetLocalInt(oPC, IntToString(FEAT_LANGUAGE_ELVEN), TRUE); + break; + } + case 16: + { + GrantLanguage(oPC, FEAT_LANGUAGE_GIANT); + SetLocalInt(oPC, IntToString(FEAT_LANGUAGE_GIANT), TRUE); + break; + } + case 17: + { + GrantLanguage(oPC, FEAT_LANGUAGE_GNOMISH); + SetLocalInt(oPC, IntToString(FEAT_LANGUAGE_GNOMISH), TRUE); + break; + } + case 18: + { + GrantLanguage(oPC, FEAT_LANGUAGE_GOBLIN); + SetLocalInt(oPC, IntToString(FEAT_LANGUAGE_GOBLIN), TRUE); + break; + } + case 19: + { + GrantLanguage(oPC, FEAT_LANGUAGE_HALFLING); + SetLocalInt(oPC, IntToString(FEAT_LANGUAGE_HALFLING), TRUE); + break; + } + case 20: + { + GrantLanguage(oPC, FEAT_LANGUAGE_IGNAN); + SetLocalInt(oPC, IntToString(FEAT_LANGUAGE_IGNAN), TRUE); + break; + } + case 21: + { + GrantLanguage(oPC, FEAT_LANGUAGE_ILLUSKAN); + SetLocalInt(oPC, IntToString(FEAT_LANGUAGE_ILLUSKAN), TRUE); + break; + } + case 22: + { + GrantLanguage(oPC, FEAT_LANGUAGE_IMASKARI); + SetLocalInt(oPC, IntToString(FEAT_LANGUAGE_IMASKARI), TRUE); + break; + } + case 23: + { + GrantLanguage(oPC, FEAT_LANGUAGE_INFERNAL); + SetLocalInt(oPC, IntToString(FEAT_LANGUAGE_INFERNAL), TRUE); + break; + } + case 24: + { + GrantLanguage(oPC, FEAT_LANGUAGE_LANTANESE); + SetLocalInt(oPC, IntToString(FEAT_LANGUAGE_LANTANESE), TRUE); + break; + } + case 25: + { + GrantLanguage(oPC, FEAT_LANGUAGE_MAZTILAN); + SetLocalInt(oPC, IntToString(FEAT_LANGUAGE_MAZTILAN), TRUE); + break; + } + case 26: + { + GrantLanguage(oPC, FEAT_LANGUAGE_MULANESE); + SetLocalInt(oPC, IntToString(FEAT_LANGUAGE_MULANESE), TRUE); + break; + } + case 27: + { + GrantLanguage(oPC, FEAT_LANGUAGE_MULHORANDI); + SetLocalInt(oPC, IntToString(FEAT_LANGUAGE_MULHORANDI), TRUE); + break; + } + case 28: + { + GrantLanguage(oPC, FEAT_LANGUAGE_ORCISH); + SetLocalInt(oPC, IntToString(FEAT_LANGUAGE_ORCISH), TRUE); + break; + } + case 29: + { + GrantLanguage(oPC, FEAT_LANGUAGE_RASHEMI); + SetLocalInt(oPC, IntToString(FEAT_LANGUAGE_RASHEMI), TRUE); + break; + } + case 30: + { + GrantLanguage(oPC, FEAT_LANGUAGE_SYLVAN); + SetLocalInt(oPC, IntToString(FEAT_LANGUAGE_SYLVAN), TRUE); + break; + } + case 31: + { + GrantLanguage(oPC, FEAT_LANGUAGE_TALFIRIC); + SetLocalInt(oPC, IntToString(FEAT_LANGUAGE_TALFIRIC), TRUE); + break; + } + case 32: + { + GrantLanguage(oPC, FEAT_LANGUAGE_TERRAN); + SetLocalInt(oPC, IntToString(FEAT_LANGUAGE_TERRAN), TRUE); + break; + } + case 33: + { + GrantLanguage(oPC, FEAT_LANGUAGE_TROGLODYTE); + SetLocalInt(oPC, IntToString(FEAT_LANGUAGE_TROGLODYTE), TRUE); + break; + } + case 34: + { + GrantLanguage(oPC, FEAT_LANGUAGE_UNDERCOMMON); + SetLocalInt(oPC, IntToString(FEAT_LANGUAGE_UNDERCOMMON), TRUE); + break; + } + } + + // Check if player is out of languages after granting this one + if (GetBonusLanguageCount(oPC) == 0) + { + SetPersistantLocalInt(oPC, "CC4_DONE", 1); + DelayCommand(0.1f, StartDynamicConversation("bg_profs_cv", oPC)); + AllowExit(DYNCONV_EXIT_FORCE_EXIT, TRUE, oPC); + } + else + { + // Rebuild stage to update available options + MarkStageNotSetUp(nStage, oPC); + SetStage(nStage, oPC); + + } + } + } + // Handle refresh + else if (nChoice == 99) + { + MarkStageNotSetUp(nStage, oPC); + SetStage(nStage, oPC); + } + } + } + + // Setup tokens for dynamic conversation system + SetupTokens(); +} + + + + +/* void main() { object oPC = GetPCSpeaker(); object oItem = EnsurePlayerDataObject(oPC); @@ -520,4 +1211,4 @@ void main() // Setup tokens for dynamic conversation system SetupTokens(); -} \ No newline at end of file +} */ \ No newline at end of file diff --git a/src/nss/bg_profs_cv.nss b/src/nss/bg_profs_cv.nss index 22fbadc..3d699e8 100644 --- a/src/nss/bg_profs_cv.nss +++ b/src/nss/bg_profs_cv.nss @@ -1,153 +1,415 @@ // bg_proficiency_cv.nss #include "inc_dynconv" #include "x2_inc_switches" +#include "inc_persist_loca" +#include "te_afflic_func" + +// Ensure the PC Data Object exists; create if missing +object EnsurePlayerDataObject(object oPC = OBJECT_SELF) +{ + object oItem = GetItemPossessedBy(oPC, "PC_Data_Object"); + if (!GetIsObjectValid(oItem)) + { + oItem = CreateItemOnObject("pc_data_object", oPC); + } + return oItem; +} + + +// prof_chk_alchemy +// Requirements: Any Standing +int CheckAlchemyProficiency(object oPC = OBJECT_SELF) +{ + return TRUE; // Always available +} +// prof_chk_anatomy +// Requirements: Upper or Middle Class Standing +int CheckAnatomyProficiency(object oPC = OBJECT_SELF) +{ + int nClass = BACKGROUND_LOWER; + nClass = GetPersistantLocalInt(oPC, "CC1"); + + return (nClass == BACKGROUND_MIDDLE || nClass == BACKGROUND_UPPER); +} + +// prof_chk_armor +// Requirements: Lower Class Standing +int CheckArmorsmithingProficiency(object oPC = OBJECT_SELF) +{ + int nClass = BACKGROUND_LOWER; + nClass = GetPersistantLocalInt(oPC, "CC1"); + return (nClass == BACKGROUND_LOWER); +} + +// prof_chk_astro +// Requirements: Middle or Upper Class Standing +int CheckAstronomyProficiency(object oPC = OBJECT_SELF) +{ + int nClass = BACKGROUND_LOWER; + nClass = GetPersistantLocalInt(oPC, "CC1"); + return (nClass == BACKGROUND_MIDDLE || nClass == BACKGROUND_UPPER); +} + +// prof_chk_carp +// Requirements: Any Standing +int CheckCarpentryProficiency(object oPC = OBJECT_SELF) +{ + return TRUE; // Always available +} + +// prof_chk_deciph +// Requirements: Middle or Upper Class Standing +int CheckDecipherScriptProficiency(object oPC = OBJECT_SELF) +{ + int nClass = BACKGROUND_LOWER; + nClass = GetPersistantLocalInt(oPC, "CC1"); + return (nClass == BACKGROUND_MIDDLE || nClass == BACKGROUND_UPPER); +} + +// prof_chk_disguis +// Requirements: Any Standing +int CheckDisguiseProficiency(object oPC = OBJECT_SELF) +{ + return TRUE; // Always available +} + +// prof_chk_fire +// Requirements: Any Standing +int CheckFiremakingProficiency(object oPC = OBJECT_SELF) +{ + return TRUE; // Always available +} + +// prof_chk_guns +// Requirements: None +int CheckGunsmithingProficiency(object oPC = OBJECT_SELF) +{ + return TRUE; // Always available +} + +// prof_chk_herb +// Requirements: Any Standing +int CheckHerbalismProficiency(object oPC = OBJECT_SELF) +{ + return TRUE; // Always available +} + +// prof_chk_hist +// Requirements: Middle or Upper Class Standing +int CheckHistoryProficiency(object oPC = OBJECT_SELF) +{ + int nClass = BACKGROUND_LOWER; + nClass = GetPersistantLocalInt(oPC, "CC1"); + return (nClass == BACKGROUND_MIDDLE || nClass == BACKGROUND_UPPER); +} + +// prof_chk_hunt +// Requirements: Any Standing +int CheckHuntingProficiency(object oPC = OBJECT_SELF) +{ + return TRUE; // Always available +} + +// prof_chk_mason +// Requirements: Middle or Lower Class Standing +int CheckMasonryProficiency(object oPC = OBJECT_SELF) +{ + int nClass = BACKGROUND_LOWER; + nClass = GetPersistantLocalInt(oPC, "CC1"); + return (nClass == BACKGROUND_LOWER || nClass == BACKGROUND_MIDDLE); +} + +// prof_chk_mining +// Requirements: Lower Class Standing +int CheckMiningProficiency(object oPC = OBJECT_SELF) +{ + int nClass = BACKGROUND_LOWER; + nClass = GetPersistantLocalInt(oPC, "CC1"); + return (nClass == BACKGROUND_LOWER); +} + +// prof_chk_obs +// Requirements: Any Standing +int CheckObservationProficiency(object oPC = OBJECT_SELF) +{ + return TRUE; // Always available +} + +// prof_chk_siege +// Requirements: Any Standing +int CheckSiegeEngineeringProficiency(object oPC = OBJECT_SELF) +{ + return TRUE; // Always available +} + +// prof_chk_smelt +// Requirements: Lower Class Standing +int CheckSmeltingProficiency(object oPC = OBJECT_SELF) +{ + int nClass = BACKGROUND_LOWER; + nClass = GetPersistantLocalInt(oPC, "CC1"); + return (nClass == BACKGROUND_LOWER); +} + +// prof_chk_tailor +// Requirements: Any Standing +int CheckTailoringProficiency(object oPC = OBJECT_SELF) +{ + return TRUE; // Always available +} + +// prof_chk_track +// Requirements: Any Standing +int CheckTrackingProficiency(object oPC = OBJECT_SELF) +{ + return TRUE; // Always available +} + +// prof_chk_wood +// Requirements: Any Standing +int CheckWoodGatheringProficiency(object oPC = OBJECT_SELF) +{ + return TRUE; // Always available +} + +// prof_chk_l +// No Options Left - shows when player has selected all proficiencies or has no slots +int CheckNoOptionsLeft(object oPC = OBJECT_SELF) +{ + int nProfCount = GetLocalInt(oPC, "PROFICIENCY_COUNT"); + int nMaxProfs = GetLocalInt(oPC, "MAX_PROFICIENCIES"); + return (nProfCount >= nMaxProfs); +} + +// Constants for stages const int STAGE_LIST = 0; +const int STAGE_CONFIRM = 1; -void main() { - object oPC = GetPCSpeaker(); - SendMessageToPC(oPC, "DEBUG: bg_proficiency_cv main() entered"); - int nValue = GetLocalInt(oPC, DYNCONV_VARIABLE); - int nStage = GetStage(oPC); - - // Required guard: abort if nValue is 0 - if (nValue == 0) return; +// Constants for choices +const int CHOICE_CONFIRM_YES = 100; +const int CHOICE_CONFIRM_NO = 101; +void main() { + object oPC = GetPCSpeaker(); + SendMessageToPC(oPC, "DEBUG: bg_prof_cv main() entered"); + int nValue = GetLocalInt(oPC, DYNCONV_VARIABLE); + int nStage = GetStage(oPC); + + // Required guard: abort if nValue is 0 + if (nValue == 0) return; + if (nValue == DYNCONV_SETUP_STAGE) { - if (!GetIsStageSetUp(nStage, oPC)) { - if (nStage == STAGE_LIST) { - SetHeader("Now you will select your proficiencies. All characters are allowed three proficiencies. Some proficiencies are only available to certain class standings."); - - // Proficiency options with condition checks and grant scripts - if (ExecuteScriptAndReturnInt("prof_chk_alchemy", oPC)) { - AddChoice("Alchemy", 1, oPC); - SetLocalString(oPC, "prof_dyn_text_1", "Allows you to combine and refine natural or unnatural elements into new and exciting combinations.\nRequirements: Any Standing"); + if (!GetIsStageSetUp(nStage, oPC)) { + if (nStage == STAGE_LIST) { + // Initialize proficiency count if not set + if (!GetLocalInt(oPC, "PROFICIENCY_COUNT")) { + SetLocalInt(oPC, "PROFICIENCY_COUNT", 0); } - if (ExecuteScriptAndReturnInt("prof_chk_anatomy", oPC)) { - AddChoice("Anatomy", 2, oPC); - SetLocalString(oPC, "prof_dyn_text_2", "This proficiency represents the study of the anatomy of living things. It enables a character to increase their effective critical hit severity by +2, as well as increase the save DC vs. death for a critical hit effect by +2. Additionally, a character with Anatomy proficiency receives a +2 bonus to heal checks when applying a bandage, and they receive a +2 bonus to hunting checks to recover an organ or other resource.\nRequirements: Upper or Middle Class Standing"); + if (!GetLocalInt(oPC, "MAX_PROFICIENCIES")) { + SetLocalInt(oPC, "MAX_PROFICIENCIES", 3); } - if (ExecuteScriptAndReturnInt("prof_chk_armor", oPC)) { - AddChoice("Armorsmithing", 3, oPC); - SetLocalString(oPC, "prof_dyn_text_3", "Allows you to craft armor from base materials.\nRequirements: Lower Class Standing"); + + int nProfCount = GetLocalInt(oPC, "PROFICIENCY_COUNT"); + string sHeader = "Now you will select your proficiencies. All characters are allowed three proficiencies. "; + sHeader += "Some proficiencies are only available to certain class standing.\n\n"; + sHeader += "Proficiencies selected: " + IntToString(nProfCount) + "/3"; + SetHeader(sHeader); + + // Check if proficiency is already selected + if (!GetLocalInt(oPC, "PROF_SELECTED_ALCHEMY") && CheckAlchemyProficiency()) { + AddChoice("Alchemy", 1, oPC); + SetLocalString(oPC, "prof_dyn_text_1", "Allows you to combine and refine natural or unnatural elements into new and exciting combinations.\nRequirements: Any Standing"); + } + if (!GetLocalInt(oPC, "PROF_SELECTED_ANATOMY") && CheckAnatomyProficiency()) { + AddChoice("Anatomy", 2, oPC); + SetLocalString(oPC, "prof_dyn_text_2", "This proficiency represents the study of the anatomy of living things. It enables a character to increase their effective critical hit severity by +2, as well as increase the save DC vs. death for a critical hit effect by +2. Additionally, a character with Anatomy proficiency receives a +2 bonus to heal checks when applying a bandage, and they receive a +2 bonus to hunting checks to recover an organ or other resource.\nRequirements: Upper or Middle Class Standing"); + } + if (!GetLocalInt(oPC, "PROF_SELECTED_ARMOR") && CheckArmorsmithingProficiency()) { + AddChoice("Armorsmithing", 3, oPC); + SetLocalString(oPC, "prof_dyn_text_3", "Allows you to craft armor from base materials.\nRequirements: Lower Class Standing"); + } + if (!GetLocalInt(oPC, "PROF_SELECTED_ASTRO") && CheckAstronomyProficiency()) { + AddChoice("Astronomy", 4, oPC); + SetLocalString(oPC, "prof_dyn_text_4", "Allows you to study celestial bodies and navigate by the stars.\nRequirements: Middle or Upper Class Standing"); + } + if (!GetLocalInt(oPC, "PROF_SELECTED_CARP") && CheckCarpentryProficiency()) { + AddChoice("Carpentry", 5, oPC); + SetLocalString(oPC, "prof_dyn_text_5", "Allows you to work wood to create placeables or other objects.\nRequirements: Any Standing"); + } + if (!GetLocalInt(oPC, "PROF_SELECTED_DECIPH") && CheckDecipherScriptProficiency()) { + AddChoice("Decipher Script", 6, oPC); + SetLocalString(oPC, "prof_dyn_text_6", "Allows you to decode and understand coded or arcane writings.\nRequirements: Middle or Upper Class Standing"); + } + if (!GetLocalInt(oPC, "PROF_SELECTED_DISGUISE") && CheckDisguiseProficiency()) { + AddChoice("Disguise", 7, oPC); + SetLocalString(oPC, "prof_dyn_text_7", "Allows you to alter your appearance and impersonate others.\nRequirements: Any Standing"); + } + if (!GetLocalInt(oPC, "PROF_SELECTED_FIRE") && CheckFiremakingProficiency()) { + AddChoice("Firemaking", 8, oPC); + SetLocalString(oPC, "prof_dyn_text_8", "Allows you to create and maintain fires in various conditions.\nRequirements: Any Standing"); + } + if (!GetLocalInt(oPC, "PROF_SELECTED_GUNS") && CheckGunsmithingProficiency()) { + AddChoice("Gunsmithing", 9, oPC); + SetLocalString(oPC, "prof_dyn_text_9", "This proficiency enables a character to make firearms from base crafting materials, as well as make modifications to a firearm without a requiring a crafting roll.\nRequirements: None"); + } + if (!GetLocalInt(oPC, "PROF_SELECTED_HERB") && CheckHerbalismProficiency()) { + AddChoice("Herbalism", 10, oPC); + SetLocalString(oPC, "prof_dyn_text_10", "Allows you to combine and refine natural or unnatural elements into new and exciting combinations.\nRequirements: Any Standing"); + } + if (!GetLocalInt(oPC, "PROF_SELECTED_HIST") && CheckHistoryProficiency()) { + AddChoice("History", 11, oPC); + SetLocalString(oPC, "prof_dyn_text_11", "Allows you to recall historical events and lore.\nRequirements: Middle or Upper Class Standing"); + } + if (!GetLocalInt(oPC, "PROF_SELECTED_HUNT") && CheckHuntingProficiency()) { + AddChoice("Hunting", 12, oPC); + SetLocalString(oPC, "prof_dyn_text_12", "Allows you to track and harvest game.\nRequirements: Any Standing"); + } + if (!GetLocalInt(oPC, "PROF_SELECTED_MASON") && CheckMasonryProficiency()) { + AddChoice("Masonry", 13, oPC); + SetLocalString(oPC, "prof_dyn_text_13", "Allows you to work stone in order to create placeables or other objects.\nRequirements: Middle or Lower Class Standing"); + } + if (!GetLocalInt(oPC, "PROF_SELECTED_MINING") && CheckMiningProficiency()) { + AddChoice("Mining", 14, oPC); + SetLocalString(oPC, "prof_dyn_text_14", "Allows you to gather useful ores, metals, and minerals from rock.\nRequirements: Lower Class Standing"); + } + if (!GetLocalInt(oPC, "PROF_SELECTED_OBS") && CheckObservationProficiency()) { + AddChoice("Observation", 15, oPC); + SetLocalString(oPC, "prof_dyn_text_15", "Allows you to notice details and perceive hidden things.\nRequirements: Any Standing"); + } + if (!GetLocalInt(oPC, "PROF_SELECTED_SIEGE") && CheckSiegeEngineeringProficiency()) { + AddChoice("Siege Engineering", 16, oPC); + SetLocalString(oPC, "prof_dyn_text_16", "Allows you to construct and operate siege weapons.\nRequirements: Any Standing"); + } + if (!GetLocalInt(oPC, "PROF_SELECTED_SMELT") && CheckSmeltingProficiency()) { + AddChoice("Smelting", 17, oPC); + SetLocalString(oPC, "prof_dyn_text_17", "Allows you to refine ores into usable metals.\nRequirements: Lower Class Standing"); + } + if (!GetLocalInt(oPC, "PROF_SELECTED_TAILOR") && CheckTailoringProficiency()) { + AddChoice("Tailoring", 18, oPC); + SetLocalString(oPC, "prof_dyn_text_18", "Allows you to craft clothing and leather goods.\nRequirements: Any Standing"); + } + if (!GetLocalInt(oPC, "PROF_SELECTED_TRACK") && CheckTrackingProficiency()) { + AddChoice("Tracking", 19, oPC); + SetLocalString(oPC, "prof_dyn_text_19", "Allows you to follow tracks and trails.\nRequirements: Any Standing"); + } + if (!GetLocalInt(oPC, "PROF_SELECTED_WOOD") && CheckWoodGatheringProficiency()) { + AddChoice("Wood Gathering", 20, oPC); + SetLocalString(oPC, "prof_dyn_text_20", "Allows you to gather wood from trees.\nRequirements: Any Standing"); + } + + // Show Done option if at least one proficiency selected + if (nProfCount > 0) { + AddChoice("Done - I have selected enough proficiencies", 21, oPC); + SetLocalString(oPC, "prof_dyn_text_21", "Finish proficiency selection and continue to the next step."); } - if (ExecuteScriptAndReturnInt("prof_chk_astro", oPC)) { - AddChoice("Astronomy", 4, oPC); - SetLocalString(oPC, "prof_dyn_text_4", "Allows you to study celestial bodies and navigate by the stars.\nRequirements: Middle or Upper Class Standing"); - } - if (ExecuteScriptAndReturnInt("prof_chk_carp", oPC)) { - AddChoice("Carpentry", 5, oPC); - SetLocalString(oPC, "prof_dyn_text_5", "Allows you to work wood to create placeables or other objects.\nRequirements: Any Standing"); - } - if (ExecuteScriptAndReturnInt("prof_chk_deciph", oPC)) { - AddChoice("Decipher Script", 6, oPC); - SetLocalString(oPC, "prof_dyn_text_6", "Allows you to decode and understand coded or arcane writings.\nRequirements: Middle or Upper Class Standing"); - } - if (ExecuteScriptAndReturnInt("prof_chk_disguis", oPC)) { - AddChoice("Disguise", 7, oPC); - SetLocalString(oPC, "prof_dyn_text_7", "Allows you to alter your appearance and impersonate others.\nRequirements: Any Standing"); - } - if (ExecuteScriptAndReturnInt("prof_chk_fire", oPC)) { - AddChoice("Firemaking", 8, oPC); - SetLocalString(oPC, "prof_dyn_text_8", "Allows you to create and maintain fires in various conditions.\nRequirements: Any Standing"); - } - if (ExecuteScriptAndReturnInt("prof_chk_guns", oPC)) { - AddChoice("Gunsmithing", 9, oPC); - SetLocalString(oPC, "prof_dyn_text_9", "This proficiency enables a character to make firearms from base crafting materials, as well as make modifications to a firearm without a requiring a crafting roll.\nRequirements: None"); - } - if (ExecuteScriptAndReturnInt("prof_chk_herb", oPC)) { - AddChoice("Herbalism", 10, oPC); - SetLocalString(oPC, "prof_dyn_text_10", "Allows you to combine and refine natural or unnatural elements into new and exciting combinations.\nRequirements: Any Standing"); - } - if (ExecuteScriptAndReturnInt("prof_chk_hist", oPC)) { - AddChoice("History", 11, oPC); - SetLocalString(oPC, "prof_dyn_text_11", "Allows you to recall historical events and lore.\nRequirements: Middle or Upper Class Standing"); - } - if (ExecuteScriptAndReturnInt("prof_chk_hunt", oPC)) { - AddChoice("Hunting", 12, oPC); - SetLocalString(oPC, "prof_dyn_text_12", "Allows you to track and harvest game.\nRequirements: Any Standing"); - } - if (ExecuteScriptAndReturnInt("prof_chk_mason", oPC)) { - AddChoice("Masonry", 13, oPC); - SetLocalString(oPC, "prof_dyn_text_13", "Allows you to work stone in order to create placeables or other objects.\nRequirements: Middle or Lower Class Standing"); - } - if (ExecuteScriptAndReturnInt("prof_chk_mining", oPC)) { - AddChoice("Mining", 14, oPC); - SetLocalString(oPC, "prof_dyn_text_14", "Allows you to gather useful ores, metals, and minerals from rock.\nRequirements: Lower Class Standing"); - } - if (ExecuteScriptAndReturnInt("prof_chk_obs", oPC)) { - AddChoice("Observation", 15, oPC); - SetLocalString(oPC, "prof_dyn_text_15", "Allows you to notice details and perceive hidden things.\nRequirements: Any Standing"); - } - if (ExecuteScriptAndReturnInt("prof_chk_siege", oPC)) { - AddChoice("Siege Engineering", 16, oPC); - SetLocalString(oPC, "prof_dyn_text_16", "Allows you to construct and operate siege weapons.\nRequirements: Any Standing"); - } - if (ExecuteScriptAndReturnInt("prof_chk_smelt", oPC)) { - AddChoice("Smelting", 17, oPC); - SetLocalString(oPC, "prof_dyn_text_17", "Allows you to refine ores into usable metals.\nRequirements: Lower Class Standing"); - } - if (ExecuteScriptAndReturnInt("prof_chk_tailor", oPC)) { - AddChoice("Tailoring", 18, oPC); - SetLocalString(oPC, "prof_dyn_text_18", "Allows you to craft clothing and leather goods.\nRequirements: Any Standing"); - } - if (ExecuteScriptAndReturnInt("prof_chk_track", oPC)) { - AddChoice("Tracking", 19, oPC); - SetLocalString(oPC, "prof_dyn_text_19", "Allows you to follow tracks and trails.\nRequirements: Any Standing"); - } - if (ExecuteScriptAndReturnInt("prof_chk_wood", oPC)) { - AddChoice("Wood Gathering", 20, oPC); - SetLocalString(oPC, "prof_dyn_text_20", "Allows you to gather wood from trees.\nRequirements: Any Standing"); - } - // No Options Left - if (ExecuteScriptAndReturnInt("prof_chk_l", oPC)) { - AddChoice("No Options Left", 21, oPC); - SetLocalString(oPC, "prof_dyn_text_21", "You have selected all available proficiencies or have no remaining slots."); - } - + + MarkStageSetUp(nStage, oPC); + SetDefaultTokens(); + } + else if (nStage == STAGE_CONFIRM) { + string sProfName = GetLocalString(oPC, "TEMP_PROF_NAME"); + string sProfDesc = GetLocalString(oPC, "TEMP_PROF_DESC"); + + string sHeader = "Are you sure you want to select " + sProfName + "?\n\n"; + sHeader += sProfDesc; + SetHeader(sHeader); + + AddChoice("Yes, select this proficiency", CHOICE_CONFIRM_YES, oPC); + AddChoice("No, go back", CHOICE_CONFIRM_NO, oPC); + MarkStageSetUp(nStage, oPC); SetDefaultTokens(); } - SetupTokens(); - } - } else { - // Handle PC responses - int nChoice = GetChoice(oPC); - if (nChoice == 21) { - // No Options Left: finalize and proceed to next step (e.g., disfigurement or final) - ExecuteScript("prof_give_nomore", oPC); - // Optionally chain to next conversation: - // StartDynamicConversation("bg_disfig_cv", oPC); - } else if (nChoice >= 1 && nChoice <= 20) { - // Grant the selected proficiency - string sGrant; - switch (nChoice) { - case 1: sGrant = "prof_give_alchemy"; break; - case 2: sGrant = "prof_give_anatomy"; break; - case 3: sGrant = "prof_give_armor"; break; - case 4: sGrant = "prof_give_astro"; break; - case 5: sGrant = "prof_give_carp"; break; - case 6: sGrant = "prof_give_deciph"; break; - case 7: sGrant = "prof_give_disgui"; break; - case 8: sGrant = "prof_give_fire"; break; - case 9: sGrant = "prof_give_guns"; break; - case 10: sGrant = "prof_give_herb"; break; - case 11: sGrant = "prof_give_hist"; break; - case 12: sGrant = "prof_give_hunt"; break; - case 13: sGrant = "prof_give_mason"; break; - case 14: sGrant = "prof_give_mine"; break; - case 15: sGrant = "prof_give_obs"; break; - case 16: sGrant = "prof_give_siege"; break; - case 17: sGrant = "prof_give_smelt"; break; - case 18: sGrant = "prof_give_tailor"; break; - case 19: sGrant = "prof_give_track"; break; - case 20: sGrant = "prof_give_wood"; break; - default: sGrant = ""; break; + } + SetupTokens(); + } else { + // Handle PC responses + int nChoice = GetChoice(oPC); + + if (nStage == STAGE_LIST) { + if (nChoice == 21) { + // Done - finalize and proceed to next step + ExecuteScript("prof_give_nomore", oPC); + } else if (nChoice >= 1 && nChoice <= 20) { + // Store proficiency info for confirmation + string sProfName; + string sProfVar; + string sGrant; + + switch (nChoice) + { + case 1: sProfName = "Alchemy"; sProfVar = "ALCHEMY"; sGrant = "prof_give_alchem"; break; + case 2: sProfName = "Anatomy"; sProfVar = "ANATOMY"; sGrant = "prof_give_anatomy"; break; + case 3: sProfName = "Armorsmithing"; sProfVar = "ARMOR"; sGrant = "prof_give_armor"; break; + case 4: sProfName = "Astronomy"; sProfVar = "ASTRO"; sGrant = "prof_give_astro"; break; + case 5: sProfName = "Carpentry"; sProfVar = "CARP"; sGrant = "prof_give_carp"; break; + case 6: sProfName = "Decipher Script"; sProfVar = "DECIPH"; sGrant = "prof_give_deciph"; break; + case 7: sProfName = "Disguise"; sProfVar = "DISGUISE"; sGrant = "prof_give_disgui"; break; + case 8: sProfName = "Firemaking"; sProfVar = "FIRE"; sGrant = "prof_give_fire"; break; + case 9: sProfName = "Gunsmithing"; sProfVar = "GUNS"; sGrant = "prof_give_guns"; break; + case 10: sProfName = "Herbalism"; sProfVar = "HERB"; sGrant = "prof_give_herb"; break; + case 11: sProfName = "History"; sProfVar = "HIST"; sGrant = "prof_give_hist"; break; + case 12: sProfName = "Hunting"; sProfVar = "HUNT"; sGrant = "prof_give_hunt"; break; + case 13: sProfName = "Masonry"; sProfVar = "MASON"; sGrant = "prof_give_mason"; break; + case 14: sProfName = "Mining"; sProfVar = "MINING"; sGrant = "prof_give_mine"; break; + case 15: sProfName = "Observation"; sProfVar = "OBS"; sGrant = "prof_give_obs"; break; + case 16: sProfName = "Siege Engineering"; sProfVar = "SIEGE"; sGrant = "prof_give_siege"; break; + case 17: sProfName = "Smelting"; sProfVar = "SMELT"; sGrant = "prof_give_smelt"; break; + case 18: sProfName = "Tailoring"; sProfVar = "TAILOR"; sGrant = "prof_give_tailor"; break; + case 19: sProfName = "Tracking"; sProfVar = "TRACK"; sGrant = "prof_give_track"; break; + case 20: sProfName = "Wood Gathering"; sProfVar = "WOOD"; sGrant = "prof_give_wood"; break; + } + + SetLocalString(oPC, "TEMP_PROF_NAME", sProfName); + SetLocalString(oPC, "TEMP_PROF_VAR", sProfVar); + SetLocalString(oPC, "TEMP_PROF_GRANT", sGrant); + SetLocalString(oPC, "TEMP_PROF_DESC", GetLocalString(oPC, "prof_dyn_text_" + IntToString(nChoice))); + + // Move to confirmation stage + SetStage(STAGE_CONFIRM, oPC); } - if (sGrant != "") ExecuteScript(sGrant, oPC); - // Return to list to allow further selections - SetStage(STAGE_LIST, oPC); } - SetStage(nStage, oPC); - } + else if (nStage == STAGE_CONFIRM) { + if (nChoice == CHOICE_CONFIRM_YES) { + // Confirm selection - grant proficiency + string sProfVar = GetLocalString(oPC, "TEMP_PROF_VAR"); + string sGrant = GetLocalString(oPC, "TEMP_PROF_GRANT"); + + // Mark as selected + SetLocalInt(oPC, "PROF_SELECTED_" + sProfVar, TRUE); + + // Increment count + int nCount = GetLocalInt(oPC, "PROFICIENCY_COUNT") + 1; + SetLocalInt(oPC, "PROFICIENCY_COUNT", nCount); + + // Grant the proficiency + ExecuteScript(sGrant, oPC); + + // Clean up temp vars + DeleteLocalString(oPC, "TEMP_PROF_NAME"); + DeleteLocalString(oPC, "TEMP_PROF_VAR"); + DeleteLocalString(oPC, "TEMP_PROF_GRANT"); + DeleteLocalString(oPC, "TEMP_PROF_DESC"); + + // Check if player has selected 3 proficiencies + if (nCount >= 3) { + // Auto-finish + ExecuteScript("prof_give_nomore", oPC); + } + else + { + // Return to list, force refresh + MarkStageNotSetUp(STAGE_CONFIRM, oPC); // Add this line + MarkStageNotSetUp(STAGE_LIST, oPC); + SetStage(STAGE_LIST, oPC); + } + } + else if (nChoice == CHOICE_CONFIRM_NO) { + // Go back to list + MarkStageNotSetUp(STAGE_CONFIRM, oPC); + SetStage(STAGE_LIST, oPC); + } + } + } } \ No newline at end of file diff --git a/src/nss/clear_bg_vars.nss b/src/nss/clear_bg_vars.nss new file mode 100644 index 0000000..5f09299 --- /dev/null +++ b/src/nss/clear_bg_vars.nss @@ -0,0 +1,79 @@ +// clear_bg_vars.nss +#include "inc_persist_loca" + +void main() +{ + object oPC = OBJECT_SELF; + + object oDataObject = GetItemPossessedBy(oPC, "PC_Data_Object"); + if (!GetIsObjectValid(oDataObject)) + { + SendMessageToPC(oPC, "No PC_Data_Object found on this character."); + return; + } + + SendMessageToPC(oPC, "Clearing character creation variables..."); + + // Clear regular local variables from PC_Data_Object + DeleteLocalInt(oDataObject, "CC0"); // Subrace + DeleteLocalInt(oDataObject, "CC1"); // Social class + DeleteLocalInt(oDataObject, "CC2"); // Background + DeleteLocalInt(oDataObject, "CC3"); // Deity + DeleteLocalInt(oDataObject, "CC4"); // Language + DeleteLocalInt(oDataObject, "CC5"); // Future + DeleteLocalInt(oDataObject, "CC6"); // Age + DeleteLocalInt(oDataObject, "CC7"); // Disfigured + + // Clear persistent variables from PC + DeletePersistantLocalInt(oPC, "CC0"); + DeletePersistantLocalInt(oPC, "CC1"); + DeletePersistantLocalInt(oPC, "CC2"); + DeletePersistantLocalInt(oPC, "CC3"); + DeletePersistantLocalInt(oPC, "CC4"); + DeletePersistantLocalInt(oPC, "CC5"); + DeletePersistantLocalInt(oPC, "CC6"); + DeletePersistantLocalInt(oPC, "CC7"); + + // Clear completion flags + DeleteLocalInt(oDataObject, "CC0_DONE"); + DeleteLocalInt(oDataObject, "CC1_DONE"); + DeleteLocalInt(oDataObject, "CC2_DONE"); + DeleteLocalInt(oDataObject, "CC3_DONE"); + DeleteLocalInt(oDataObject, "CC4_DONE"); + DeleteLocalInt(oDataObject, "CC5_DONE"); + DeleteLocalInt(oDataObject, "CC6_DONE"); + DeleteLocalInt(oDataObject, "CC7_DONE"); + + DeletePersistantLocalInt(oPC, "CC0_DONE"); + DeletePersistantLocalInt(oPC, "CC1_DONE"); + DeletePersistantLocalInt(oPC, "CC2_DONE"); + DeletePersistantLocalInt(oPC, "CC3_DONE"); + DeletePersistantLocalInt(oPC, "CC4_DONE"); + DeletePersistantLocalInt(oPC, "CC5_DONE"); + DeletePersistantLocalInt(oPC, "CC6_DONE"); + DeletePersistantLocalInt(oPC, "CC7_DONE"); + + // Clear BG_Select variable + DeletePersistantLocalInt(oPC, "BG_Select"); + DeleteLocalInt(oDataObject, "BG_Select"); + + DeleteLocalString(oPC, "TEMP_PROF_NAME"); + DeleteLocalString(oPC, "TEMP_PROF_VAR"); + DeleteLocalString(oPC, "TEMP_PROF_GRANT"); + DeleteLocalString(oPC, "TEMP_PROF_DESC"); + DeleteLocalString(oPC, "ARR_PROF"); + DeletePersistantLocalString(oPC, "ARR_PROF"); + DeleteLocalInt(oPC, "PROFICIENCY_COUNT"); + DeletePersistantLocalInt(oPC, "PROFICIENCY_COUNT"); + + // Clear LANGUAGE_XX variables (new system) + int i; + for (i = 0; i < 20; i++) + { + string sSlot = "LANGUAGE_" + (i < 10 ? "0" : "") + IntToString(i); + DeleteLocalInt(oDataObject, sSlot); + DeletePersistantLocalInt(oPC, sSlot); + } + + SendMessageToPC(oPC, "Character creation variables cleared."); +} \ No newline at end of file