diff --git a/src/dlg/cv_background.dlg.json b/src/dlg/cv_background.dlg.json new file mode 100644 index 0000000..2415736 --- /dev/null +++ b/src/dlg/cv_background.dlg.json @@ -0,0 +1,305 @@ +{ + "__data_type": "DLG ", + "DelayEntry": { + "type": "dword", + "value": 0 + }, + "DelayReply": { + "type": "dword", + "value": 0 + }, + "EndConverAbort": { + "type": "resref", + "value": "nw_walk_wp" + }, + "EndConversation": { + "type": "resref", + "value": "nw_walk_wp" + }, + "EntryList": { + "type": "list", + "value": [ + { + "__struct_id": 0, + "ActionParams": { + "type": "list", + "value": [] + }, + "Animation": { + "type": "dword", + "value": 0 + }, + "AnimLoop": { + "type": "byte", + "value": 1 + }, + "Comment": { + "type": "cexostring", + "value": "" + }, + "Delay": { + "type": "dword", + "value": 4294967295 + }, + "Quest": { + "type": "cexostring", + "value": "" + }, + "RepliesList": { + "type": "list", + "value": [ + { + "__struct_id": 0, + "Active": { + "type": "resref", + "value": "" + }, + "ConditionParams": { + "type": "list", + "value": [] + }, + "Index": { + "type": "dword", + "value": 0 + }, + "IsChild": { + "type": "byte", + "value": 0 + } + } + ] + }, + "Script": { + "type": "resref", + "value": "" + }, + "Sound": { + "type": "resref", + "value": "" + }, + "Speaker": { + "type": "cexostring", + "value": "" + }, + "Text": { + "type": "cexolocstring", + "value": { + "0": "Welcome to Spellplague: The Rebirth. We hope you enjoy your stay. The world's lore takes place in 1480, when Mystra's just returned and the Shadow Plague is coming into effect. You will now choose background feats that define your character! These have societal and mechanical effects so choose what best fits your character." + } + } + }, + { + "__struct_id": 1, + "ActionParams": { + "type": "list", + "value": [] + }, + "Animation": { + "type": "dword", + "value": 0 + }, + "AnimLoop": { + "type": "byte", + "value": 1 + }, + "Comment": { + "type": "cexostring", + "value": "" + }, + "Delay": { + "type": "dword", + "value": 4294967295 + }, + "Quest": { + "type": "cexostring", + "value": "" + }, + "RepliesList": { + "type": "list", + "value": [ + { + "__struct_id": 0, + "Active": { + "type": "resref", + "value": "" + }, + "ConditionParams": { + "type": "list", + "value": [] + }, + "Index": { + "type": "dword", + "value": 1 + }, + "IsChild": { + "type": "byte", + "value": 0 + } + } + ] + }, + "Script": { + "type": "resref", + "value": "" + }, + "Sound": { + "type": "resref", + "value": "" + }, + "Speaker": { + "type": "cexostring", + "value": "" + }, + "Text": { + "type": "cexolocstring", + "value": { + "0": "The Selection process is as follows:\n1. Subrace/Ethnicity, 2. Class Standing, 3. Background, 4. Deity, 5. Language, 6. Proficiencies, 7 Final Touches.\n\nIf for any reason you accidentally exit out of this conversation, pull the lever next to you to continue your progress from the selection point you left off at." + } + } + } + ] + }, + "NumWords": { + "type": "dword", + "value": 106 + }, + "PreventZoomIn": { + "type": "byte", + "value": 0 + }, + "ReplyList": { + "type": "list", + "value": [ + { + "__struct_id": 0, + "ActionParams": { + "type": "list", + "value": [] + }, + "Animation": { + "type": "dword", + "value": 0 + }, + "AnimLoop": { + "type": "byte", + "value": 1 + }, + "Comment": { + "type": "cexostring", + "value": "" + }, + "Delay": { + "type": "dword", + "value": 4294967295 + }, + "EntriesList": { + "type": "list", + "value": [ + { + "__struct_id": 0, + "Active": { + "type": "resref", + "value": "" + }, + "ConditionParams": { + "type": "list", + "value": [] + }, + "Index": { + "type": "dword", + "value": 1 + }, + "IsChild": { + "type": "byte", + "value": 0 + } + } + ] + }, + "Quest": { + "type": "cexostring", + "value": "" + }, + "Script": { + "type": "resref", + "value": "" + }, + "Sound": { + "type": "resref", + "value": "" + }, + "Text": { + "type": "cexolocstring", + "value": { + "0": "Next." + } + } + }, + { + "__struct_id": 1, + "ActionParams": { + "type": "list", + "value": [] + }, + "Animation": { + "type": "dword", + "value": 0 + }, + "AnimLoop": { + "type": "byte", + "value": 1 + }, + "Comment": { + "type": "cexostring", + "value": "" + }, + "Delay": { + "type": "dword", + "value": 4294967295 + }, + "EntriesList": { + "type": "list", + "value": [] + }, + "Quest": { + "type": "cexostring", + "value": "" + }, + "Script": { + "type": "resref", + "value": "cv_background" + }, + "Sound": { + "type": "resref", + "value": "" + }, + "Text": { + "type": "cexolocstring", + "value": { + "0": "Understood." + } + } + } + ] + }, + "StartingList": { + "type": "list", + "value": [ + { + "__struct_id": 0, + "Active": { + "type": "resref", + "value": "" + }, + "ConditionParams": { + "type": "list", + "value": [] + }, + "Index": { + "type": "dword", + "value": 0 + } + } + ] + } +} diff --git a/src/nss/bg_age_cv.nss b/src/nss/bg_age_cv.nss index f39b3ba..4675d82 100644 --- a/src/nss/bg_age_cv.nss +++ b/src/nss/bg_age_cv.nss @@ -1,11 +1,8 @@ // bg_age_cv.nss -#include "inc_dynconv" +#include "bg_inc_dynconv" #include "x2_inc_switches" -#include "inc_persist_loca" +#include "bg_inc_p_locals" #include "te_afflic_func" - -const int STAGE_LIST = 0; -const int STAGE_CONFIRM = 1; // Ensure the PC Data Object exists; create if missing object EnsurePlayerDataObject(object oPC) @@ -20,124 +17,247 @@ object EnsurePlayerDataObject(object oPC) return oItem; } - -void main() -{ - object oPC = GetPCSpeaker(); - SendMessageToPC(oPC, "DEBUG: bg_age_cv main() entered"); +const int STAGE_LIST = 0; +const int STAGE_CONFIRM = 1; - object oItem = EnsurePlayerDataObject(oPC); - - 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 for the finishing touches. If you would like to apply an age modifier to your character, you may do so. Aging affects are as follows:\n" + - "Young Age/Opt Out: No effects\n" + - "Middle Age: -1 STR, -1 DEX, -1 CON, +1 INT, +1 WIS, +1 CHA\n" + - "Old Age: -2 STR, -2 DEX, -2 CON, +2 INT, +2 WIS, +2 CHA\n" + - "Venerable Age: -3 STR, -3 DEX, -3 CON, +3 INT, +3 WIS, +3 CHA"); - - // Store descriptive text for each age choice - SetLocalString(oPC, "age_text_0", "You are in the prime of your youth, with physical abilities at their peak and mental faculties still developing. This represents a character starting their adventuring career in their late teens or early twenties, with no modifiers to abilities."); - SetLocalString(oPC, "age_text_1", "You have reached middle age, where the physical toll of years begins to show, but experience and wisdom have sharpened your mind. Your body has begun to slow (-1 STR, -1 DEX, -1 CON) while your mental faculties have matured (+1 INT, +1 WIS, +1 CHA)."); - SetLocalString(oPC, "age_text_2", "The weight of many decades rests upon you. Physical decline is more pronounced (-2 STR, -2 DEX, -2 CON) but your accumulated knowledge and insight have grown significantly (+2 INT, +2 WIS, +2 CHA). You are a veteran of life's trials."); - SetLocalString(oPC, "age_text_3", "You have lived a very long life, and your body shows the full extent of your years (-3 STR, -3 DEX, -3 CON). However, your mind is a repository of vast knowledge and profound wisdom (+3 INT, +3 WIS, +3 CHA). You are truly an elder whose experience spans generations."); - - AddChoice("Young Age / Opt Out", 0, oPC); - AddChoice("Middle Age", 1, oPC); - AddChoice("Old Age", 2, oPC); - AddChoice("Venerable Age", 3, oPC); - MarkStageSetUp(nStage, oPC); - SetDefaultTokens(); - } - else if (nStage == STAGE_CONFIRM) - { - int nSelected = GetLocalInt(oPC, "age_selected"); - string sAgeText = GetLocalString(oPC, "age_text_" + IntToString(nSelected)); - string sPrompt = sAgeText + "\n\nAre you sure you want to apply this age modifier?"; - SetHeader(sPrompt); - AddChoice("Yes", nSelected, oPC); - AddChoice("No", -1, oPC); - MarkStageSetUp(nStage, oPC); - SetDefaultTokens(); - } - } - SetupTokens(); - } - else - { - int nChoice = GetChoice(oPC); - if (nStage == STAGE_LIST) - { - SetLocalInt(oPC, "age_selected", nChoice); - SetStage(STAGE_CONFIRM, oPC); - } - else if (nStage == STAGE_CONFIRM) - { - if (nChoice >= 0) - { - string sGrant; - switch (nChoice) - { - default: - case 0: - {// youthful - //sGrant = "te_bg_a_n"; - SetLocalInt(oItem, "BG_Select",7); - SetPersistantLocalInt(oPC, "BG_Select", 7); - SetLocalInt(oItem, "CC6", AGE_CATEGORY_YOUTHFUL); - SetPersistantLocalInt(oPC, "CC6", AGE_CATEGORY_YOUTHFUL); - break; - } - case 1: - {// middle-aged - //sGrant = "te_bg_a_m"; - SetLocalInt(oItem, "BG_Select",7); - SetPersistantLocalInt(oPC, "BG_Select", 7); - SetLocalInt(oItem, "CC6", AGE_CATEGORY_MIDDLE); - SetPersistantLocalInt(oPC, "CC6", AGE_CATEGORY_MIDDLE); - break; - } - case 2: - { - //sGrant = "te_bg_a_o"; - SetLocalInt(oItem, "BG_Select",7); - SetPersistantLocalInt(oPC, "BG_Select", 7); - SetLocalInt(oItem, "CC6", AGE_CATEGORY_OLD); - SetPersistantLocalInt(oPC, "CC6", AGE_CATEGORY_OLD); - break; - } - case 3: - { - //sGrant = "te_bg_a_v"; break; - SetLocalInt(oItem, "BG_Select",7); - SetPersistantLocalInt(oPC, "BG_Select", 7); - SetLocalInt(oItem, "CC6", AGE_CATEGORY_VENERABLE); - SetPersistantLocalInt(oPC, "CC6", AGE_CATEGORY_VENERABLE); - } - } - //if (sGrant != "") ExecuteScript(sGrant, oPC); - // Conversation ends naturally - } - else - { - SetStage(STAGE_LIST, oPC); - } - } - SetStage(nStage, oPC); - } +void main() +{ + object oPC = GetPCSpeaker(); + object oItem = EnsurePlayerDataObject(oPC); + //SendMessageToPC(oPC, "DEBUG: bg_age_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 for the finishing touches. If you would like to apply an age modifier to your character, you may do so. Aging affects are as follows:\n" + + "Young Age/Opt Out: No effects\n" + + "Middle Age: -1 STR, -1 DEX, -1 CON, +1 INT, +1 WIS, +1 CHA\n" + + "Old Age: -2 STR, -2 DEX, -2 CON, +2 INT, +2 WIS, +2 CHA\n" + + "Venerable Age: -3 STR, -3 DEX, -3 CON, +3 INT, +3 WIS, +3 CHA"); + + // Store descriptive text for each age choice + SetLocalString(oPC, "age_text_0", "You are in the prime of your youth, with physical abilities at their peak and mental faculties still developing. This represents a character starting their adventuring career in their late teens or early twenties, with no modifiers to abilities."); + SetLocalString(oPC, "age_text_1", "You have reached middle age, where the physical toll of years begins to show, but experience and wisdom have sharpened your mind. Your body has begun to slow (-1 STR, -1 DEX, -1 CON) while your mental faculties have matured (+1 INT, +1 WIS, +1 CHA)."); + SetLocalString(oPC, "age_text_2", "The weight of many decades rests upon you. Physical decline is more pronounced (-2 STR, -2 DEX, -2 CON) but your accumulated knowledge and insight have grown significantly (+2 INT, +2 WIS, +2 CHA). You are a veteran of life's trials."); + SetLocalString(oPC, "age_text_3", "You have lived a very long life, and your body shows the full extent of your years (-3 STR, -3 DEX, -3 CON). However, your mind is a repository of vast knowledge and profound wisdom (+3 INT, +3 WIS, +3 CHA). You are truly an elder whose experience spans generations."); + + AddChoice("Young Age / Opt Out", 0, oPC); + AddChoice("Middle Age", 1, oPC); + AddChoice("Old Age", 2, oPC); + AddChoice("Venerable Age", 3, oPC); + MarkStageSetUp(nStage, oPC); + SetDefaultTokens(); + } + else if (nStage == STAGE_CONFIRM) + { + int nSelected = GetLocalInt(oPC, "age_selected"); + string sAgeText = GetLocalString(oPC, "age_text_" + IntToString(nSelected)); + string sPrompt = sAgeText + "\n\nAre you sure you want to apply this age modifier?"; + SetHeader(sPrompt); + AddChoice("Yes", nSelected, oPC); + AddChoice("No", -1, oPC); + MarkStageSetUp(nStage, oPC); + SetDefaultTokens(); + } + } + SetupTokens(); + } + else + { + int nChoice = GetChoice(oPC); + if (nStage == STAGE_LIST) + { + SetLocalInt(oPC, "cs_selected", nChoice); + nStage = STAGE_CONFIRM; // update local nStage + } + else if (nStage == STAGE_CONFIRM) + { + if (nChoice >= 0) + { + string sGrant; + switch (nChoice) + { + default: + case 0: + {// youthful + SetLocalInt(oItem, "BG_Select",7); + SetPersistantLocalInt(oPC, "BG_Select", 7); + SetLocalInt(oItem, "CC6", AGE_CATEGORY_YOUTHFUL); + SetPersistantLocalInt(oPC, "CC6", AGE_CATEGORY_YOUTHFUL); + break; + } + case 1: + {// middle-aged + SetLocalInt(oItem, "BG_Select",7); + SetPersistantLocalInt(oPC, "BG_Select", 7); + SetLocalInt(oItem, "CC6", AGE_CATEGORY_MIDDLE); + SetPersistantLocalInt(oPC, "CC6", AGE_CATEGORY_MIDDLE); + break; + } + case 2: + { + SetLocalInt(oItem, "BG_Select",7); + SetPersistantLocalInt(oPC, "BG_Select", 7); + SetLocalInt(oItem, "CC6", AGE_CATEGORY_OLD); + SetPersistantLocalInt(oPC, "CC6", AGE_CATEGORY_OLD); + break; + } + case 3: + { + SetLocalInt(oItem, "BG_Select",7); + SetPersistantLocalInt(oPC, "BG_Select", 7); + SetLocalInt(oItem, "CC6", AGE_CATEGORY_VENERABLE); + SetPersistantLocalInt(oPC, "CC6", AGE_CATEGORY_VENERABLE); + break; + } + } + ClearCurrentStage(oPC); + SetPersistantLocalInt(oPC, "CC6_DONE", 1); + SetPersistantLocalInt(oPC, "Background_Stage", 7); + SetLocalInt(oItem, "CC6_DONE", 1); + AllowExit(DYNCONV_EXIT_FORCE_EXIT, TRUE, oPC); + //SendMessageToPC(oPC, "DEBUG: firing bg_disfig_cv"); + DelayCommand(0.1f, StartDynamicConversation("bg_disfig_cv", oPC)); + } + else + { + SetStage(STAGE_LIST, oPC); + } + } + + SetStage(nStage, oPC); + } } - + +/* + +void main() +{ + object oPC = GetPCSpeaker(); + SendMessageToPC(oPC, "DEBUG: bg_age_cv main() entered"); + + object oItem = EnsurePlayerDataObject(oPC); + + 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 for the finishing touches. If you would like to apply an age modifier to your character, you may do so. Aging affects are as follows:\n" + + "Young Age/Opt Out: No effects\n" + + "Middle Age: -1 STR, -1 DEX, -1 CON, +1 INT, +1 WIS, +1 CHA\n" + + "Old Age: -2 STR, -2 DEX, -2 CON, +2 INT, +2 WIS, +2 CHA\n" + + "Venerable Age: -3 STR, -3 DEX, -3 CON, +3 INT, +3 WIS, +3 CHA"); + + // Store descriptive text for each age choice + SetLocalString(oPC, "age_text_0", "You are in the prime of your youth, with physical abilities at their peak and mental faculties still developing. This represents a character starting their adventuring career in their late teens or early twenties, with no modifiers to abilities."); + SetLocalString(oPC, "age_text_1", "You have reached middle age, where the physical toll of years begins to show, but experience and wisdom have sharpened your mind. Your body has begun to slow (-1 STR, -1 DEX, -1 CON) while your mental faculties have matured (+1 INT, +1 WIS, +1 CHA)."); + SetLocalString(oPC, "age_text_2", "The weight of many decades rests upon you. Physical decline is more pronounced (-2 STR, -2 DEX, -2 CON) but your accumulated knowledge and insight have grown significantly (+2 INT, +2 WIS, +2 CHA). You are a veteran of life's trials."); + SetLocalString(oPC, "age_text_3", "You have lived a very long life, and your body shows the full extent of your years (-3 STR, -3 DEX, -3 CON). However, your mind is a repository of vast knowledge and profound wisdom (+3 INT, +3 WIS, +3 CHA). You are truly an elder whose experience spans generations."); + + AddChoice("Young Age / Opt Out", 0, oPC); + AddChoice("Middle Age", 1, oPC); + AddChoice("Old Age", 2, oPC); + AddChoice("Venerable Age", 3, oPC); + MarkStageSetUp(nStage, oPC); + SetDefaultTokens(); + } + else if (nStage == STAGE_CONFIRM) + { + int nSelected = GetLocalInt(oPC, "age_selected"); + string sAgeText = GetLocalString(oPC, "age_text_" + IntToString(nSelected)); + string sPrompt = sAgeText + "\n\nAre you sure you want to apply this age modifier?"; + SetHeader(sPrompt); + AddChoice("Yes", nSelected, oPC); + AddChoice("No", -1, oPC); + MarkStageSetUp(nStage, oPC); + SetDefaultTokens(); + } + } + SetupTokens(); + } + else + { + int nChoice = GetChoice(oPC); + if (nStage == STAGE_LIST) + { + SetLocalInt(oPC, "age_selected", nChoice); + SetStage(STAGE_CONFIRM, oPC); + } + else if (nStage == STAGE_CONFIRM) + { + if (nChoice >= 0) + { + string sGrant; + switch (nChoice) + { + default: + case 0: + {// youthful + SetLocalInt(oItem, "BG_Select",7); + SetPersistantLocalInt(oPC, "BG_Select", 7); + SetLocalInt(oItem, "CC6", AGE_CATEGORY_YOUTHFUL); + SetPersistantLocalInt(oPC, "CC6", AGE_CATEGORY_YOUTHFUL); + break; + } + case 1: + {// middle-aged + SetLocalInt(oItem, "BG_Select",7); + SetPersistantLocalInt(oPC, "BG_Select", 7); + SetLocalInt(oItem, "CC6", AGE_CATEGORY_MIDDLE); + SetPersistantLocalInt(oPC, "CC6", AGE_CATEGORY_MIDDLE); + break; + } + case 2: + { + SetLocalInt(oItem, "BG_Select",7); + SetPersistantLocalInt(oPC, "BG_Select", 7); + SetLocalInt(oItem, "CC6", AGE_CATEGORY_OLD); + SetPersistantLocalInt(oPC, "CC6", AGE_CATEGORY_OLD); + break; + } + case 3: + { + SetLocalInt(oItem, "BG_Select",7); + SetPersistantLocalInt(oPC, "BG_Select", 7); + SetLocalInt(oItem, "CC6", AGE_CATEGORY_VENERABLE); + SetPersistantLocalInt(oPC, "CC6", AGE_CATEGORY_VENERABLE); + break; + } + } + // Chain to disfigurement conversation + SetPersistantLocalInt(oPC, "CC6_DONE", 1); + SetLocalInt(oItem, "CC6_DONE", 1); + AllowExit(DYNCONV_EXIT_FORCE_EXIT, TRUE, oPC); + SendMessageToPC(oPC, "DEBUG: firing bg_disfig_cv"); + DelayCommand(0.1f, StartDynamicConversation("bg_disfig_cv", oPC)); + } + else + { + SetStage(STAGE_LIST, oPC); + } + } + + SetStage(nStage, oPC); + } +} + */ /* void main() { object oPC = GetPCSpeaker(); SendMessageToPC(oPC, "DEBUG: bg_age_cv main() entered"); diff --git a/src/nss/bg_apply.nss b/src/nss/bg_apply.nss new file mode 100644 index 0000000..6221bdc --- /dev/null +++ b/src/nss/bg_apply.nss @@ -0,0 +1,290 @@ +// LordValinar's Scripts - Background Application (12/18/2023) +// ============================================================================= +/* + The final process, to take all of the saved settings from character + creation and apply them here. (This was to prevent exploits) +*/ +// ============================================================================= +#include "nwnx_creature" +#include "te_afflic_func" +#include "lv_inc" +#include "bg_inc_p_locals" + +void main() +{ + object oPC = OBJECT_SELF; // from ExecuteScript() + object oItem = GetItemPossessedBy(oPC, "PC_Data_Object"); + int nSubrace = GetLocalInt(oItem, "CC0"); /**/ DeleteLocalInt(oItem, "CC0"); + int nClass = GetLocalInt(oItem, "CC1"); /**/ DeleteLocalInt(oItem, "CC1"); + int nBackground = GetLocalInt(oItem, "CC2"); /**/ DeleteLocalInt(oItem, "CC2"); + int nDeity = GetLocalInt(oItem, "CC3"); /**/ DeleteLocalInt(oItem, "CC3"); + int nAge = GetLocalInt(oItem, "CC6"); /**/ DeleteLocalInt(oItem, "CC6"); + int bDisfigured = GetLocalInt(oItem, "CC7"); /**/ DeleteLocalInt(oItem, "CC7"); + // Stats + int iCL = GetHitDice(oPC); + int iSTR = GetAbilityScore(oPC, ABILITY_STRENGTH, TRUE); + int iDEX = GetAbilityScore(oPC, ABILITY_DEXTERITY, TRUE); + int iCON = GetAbilityScore(oPC, ABILITY_CONSTITUTION, TRUE); + int iINT = GetAbilityScore(oPC, ABILITY_INTELLIGENCE, TRUE); + int iWIS = GetAbilityScore(oPC, ABILITY_WISDOM, TRUE); + int iCHA = GetAbilityScore(oPC, ABILITY_CHARISMA, TRUE); + // Skills + int iHide = GetSkillRank(SKILL_HIDE, oPC, TRUE); + int iBluff = GetSkillRank(SKILL_BLUFF, oPC, TRUE); + int iSpot = GetSkillRank(SKILL_SPOT, oPC, TRUE); + int iListen = GetSkillRank(SKILL_LISTEN, oPC, TRUE); + int iLore = GetSkillRank(SKILL_LORE, oPC, TRUE); + int iMoveSil = GetSkillRank(SKILL_MOVE_SILENTLY, oPC, TRUE); + int iSpellCraft = GetSkillRank(SKILL_SPELLCRAFT, oPC, TRUE); + // Effects + effect eSpellResist = EffectSpellResistanceIncrease(11+iCL); + + // Step 0: Subrace/Ethnicity + if (nSubrace > 0) NWNX_Creature_AddFeatByLevel(oPC, nSubrace, 1); + switch (nSubrace) + { + case ETHNICITY_TETHYRIAN: case ETHNICITY_CALISHITE: { + SetLocalInt(oItem, "23", 1); // Language: Alzhedo + } break; + case ETHNICITY_CHONDATHAN: { + SetLocalInt(oItem, "53", 1); // Language: Chondathan + } break; + case ETHNICITY_DAMARAN: { + SetLocalInt(oItem, "56", 1); // Language: Damaran + } break; + case 1445: { // Talfirian + SetLocalInt(oItem, "38", 1); // Language: Talfiric + } break; + case ETHNICITY_ILLUSKAN: { + SetLocalInt(oItem, "22", 1); // Language: Illuskan + } break; + case 1447: { // Imaskari + SetLocalInt(oItem, "46", 1); // Language: Undercommon + } break; + case ETHNICITY_MULAN: { + SetLocalInt(oItem, "27", 1); // Language: Mulanese + } break; + case ETHNICITY_RASHEMI: { + SetLocalInt(oItem, "30", 1); // Langauge: Rashemi + } break; + case BACKGROUND_TIEFLING: { + SetLocalInt(oItem, "iPCSubrace", 11); + SetLocalInt(oItem, "PC_ECL", 1); + SetSubRace(oPC, "Tiefling"); + NWNX_Creature_AddFeatByLevel(oPC,228,1); // Darkvision + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_INTELLIGENCE, iINT +2); + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_DEXTERITY, iDEX +2); + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_CHARISMA, iCHA -2); + NWNX_Creature_SetSkillRank(oPC, SKILL_BLUFF, iBluff +2); + NWNX_Creature_SetSkillRank(oPC, SKILL_HIDE, iHide+2); + } break; + case BACKGROUND_NAT_LYCAN: { + SetLocalInt(oItem, "PC_ECL", 2); + if (GetRacialType(oPC) == RACIAL_TYPE_ELF) { + SetSubRace(oPC, "Lythari"); + } else { + SetSubRace(oPC, "Natural Lycanthrope"); + } + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_WISDOM, iWIS + 2); + } break; + case BACKGROUND_AASIMAR: { + SetLocalInt(oItem, "iPCSubrace", 10); + SetLocalInt(oItem, "PC_ECL",1); + SetSubRace(oPC, "Aasimar"); + NWNX_Creature_AddFeatByLevel(oPC,228,1); // Darkvision + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_WISDOM, iWIS +2); + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_CHARISMA, iCHA +2); + NWNX_Creature_SetSkillRank(oPC, SKILL_SPOT, iSpot +2); + NWNX_Creature_SetSkillRank(oPC, SKILL_LISTEN, iListen+2); + } break; + case BACKGROUND_GOLD_DWARF: { + SetLocalInt(oItem, "PC_ECL", 0); + SetSubRace(oPC, "Gold Dwarf"); + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_CHARISMA, iCHA +4); + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_DEXTERITY, iDEX -2); + } break; + case BACKGROUND_GREY_DWARF: { + SetLocalInt(oItem, "PC_ECL", 2); + SetSubRace(oPC, "Grey Dwarf"); + NWNX_Creature_SetSkillRank(oPC, SKILL_MOVE_SILENTLY, iMoveSil +4); + AddSubraceEffect(oPC, EffectImmunity(IMMUNITY_TYPE_POISON)); + AddSubraceEffect(oPC, EffectImmunity(IMMUNITY_TYPE_PARALYSIS)); + SetLocalInt(oItem, "64", 1); // Language: Duergar + SetLocalInt(oItem, "46", 1); // Language: Undercommon + } break; + case BACKGROUND_SHIELD_DWARF: { + SetLocalInt(oItem, "PC_ECL", 0); + SetSubRace(oPC, "Shield Dwarf"); + } break; + case BACKGROUND_COPPER_ELF: { + SetLocalInt(oItem, "PC_ECL", 0); + SetSubRace(oPC, "Copper Elf"); + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_STRENGTH, iSTR +2); + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_INTELLIGENCE, iINT -2); + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_CHARISMA, iCHA -2); + } break; + case BACKGROUND_DARK_ELF: { + SetLocalInt(oItem, "PC_ECL", 2); + SetSubRace(oPC, "Dark Elf"); + NWNX_Creature_AddFeatByLevel(oPC, 228, 1); // Darkvision + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_INTELLIGENCE, iINT +2); + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_CHARISMA, iCHA +2); + AddSubraceEffect(oPC, eSpellResist); + SetLocalInt(oItem, "81", 1); // Language: Drow + SetLocalInt(oItem, "46", 1); // Language: Undercommon + SetLocalInt(oItem, "13", 1); // Language: Drow Sign + } break; + case 1458: { // BACKGROUND_ELF_FEYRI + SetLocalInt(oItem, "PC_ECL", 3); + SetSubRace(oPC, "Fey'ri"); + NWNX_Creature_AddFeatByLevel(oPC, 228, 1); // Darkvision + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_DEXTERITY, iDEX +2); + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_CONSTITUTION, iCON -2); + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_INTELLIGENCE, iINT +2); + NWNX_Creature_SetSkillRank(oPC, SKILL_BLUFF, iBluff +2); + NWNX_Creature_SetSkillRank(oPC, SKILL_HIDE, iHide +2); + } break; + case BACKGROUND_GREEN_ELF: { + SetLocalInt(oItem, "PC_ECL", 0); + SetSubRace(oPC, "Green Elf"); + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_CONSTITUTION, iCON +4); + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_INTELLIGENCE, iINT -2); + } break; + case BACKGROUND_GOLD_ELF: { + SetLocalInt(oItem, "PC_ECL", 0); + SetSubRace(oPC, "Gold Elf"); + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_DEXTERITY, iDEX -2); + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_INTELLIGENCE, iINT +2); + } break; + case BACKGROUND_SILVER_ELF: { + SetLocalInt(oItem, "PC_ECL", 0); + SetSubRace(oPC, "Silver Elf"); + } break; + case 1452: { // BACKGROUND_GNOME_DEEP + SetLocalInt(oItem, "PC_ECL", 2); + SetSubRace(oPC, "Deep Gnome"); + NWNX_Creature_AddFeatByLevel(oPC,nSubrace,1); + NWNX_Creature_AddFeatByLevel(oPC,228,1); // Darkvision + NWNX_Creature_AddFeatByLevel(oPC,227,1); // Stonecunning + AddSubraceEffect(oPC, eSpellResist); + + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_DEXTERITY, iDEX +2); + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_CONSTITUTION, iCON -4); + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_WISDOM, iWIS +2); + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_CHARISMA, iCHA -4); + NWNX_Creature_SetSkillRank(oPC, SKILL_HIDE, iHide +2); + SetLocalInt(oItem,"46",1); // Language: Undercommon + } break; + case 1453: { // BACKGROUND_GNOME_FOREST + SetLocalInt(oItem, "PC_ECL", 1); + SetSubRace(oPC, "Forest Gnome"); + NWNX_Creature_SetSkillRank(oPC, SKILL_HIDE, iHide + 4); + SetLocalInt(oItem, "8", 1); // Language: Animals + } break; + case 1454: { // BACKGROUND_GNOME_ROCK + SetLocalInt(oItem, "PC_ECL", 0); + SetSubRace(oPC, "Rock Gnome"); + } break; + case 1455: { // BACKGROUND_HALFLING_GHOSTWISE + SetLocalInt(oItem, "PC_ECL", 0); + SetSubRace(oPC, "Ghostwise Halfling"); + } break; + case 1456: { // BACKGROUND_HALFLING_LIGHTFOOT + SetLocalInt(oItem, "PC_ECL", 0); + SetSubRace(oPC, "Lightfoot Halfling"); + } break; + case 1457: { // BACKGROUND_HALFLING_STRONGHEART + SetLocalInt(oItem, "PC_ECL", 0); + SetSubRace(oPC, "Strongheart Halfling"); + } break; + } + + // Step 1: Class Adjustments + if (nClass > 0) NWNX_Creature_AddFeatByLevel(oPC, nClass, 1); + + // Step 2: Background Adjustments + if (nBackground > 0) NWNX_Creature_AddFeatByLevel(oPC, nBackground, 1); + switch (nBackground) + { + case BACKGROUND_CALISHITE_TRAINED: + SetLocalInt(oItem, "23", 1); // Language: Alzhedo + break; + case BACKGROUND_CIRCLE_BORN: + SetLocalInt(oItem, "8", 1); // Language: Animals + break; + case BACKGROUND_OCCULTIST: { + NWNX_Creature_AddFeatByLevel(oPC, PROFICIENCY_ASTROLOGY, 1); + NWNX_Creature_SetSkillRank(oPC, SKILL_LORE, iLore +2); + NWNX_Creature_SetSkillRank(oPC, SKILL_SPELLCRAFT, iSpellCraft +2); + } break; + case BACKGROUND_TALFIRIAN: + SetLocalInt(oItem, "38", 1); // Language: Talfirian + break; + } + + // Step 4: Deity Adjustments + if (nDeity > 0) NWNX_Creature_AddFeat(oPC, nDeity); + + // Step 5: Languages + string sArr = GetLocalString(oItem, "ARR_LANGUAGES"); + int i; for (i = 0; i < ArrayLength(sArr); i++) + { + string lang = ArrayParse(sArr, i); + SetLocalInt(oItem, lang, 1); + } + DeleteLocalString(oItem, "ARR_LANGUAGES"); + + // Step 6: Proficiencies + sArr = GetLocalString(oItem, "ARR_PROF"); + for (i = 0; i < ArrayLength(sArr); i++) + { + int nFeat = StringToInt(ArrayParse(sArr, i)); + if (nFeat > 0) NWNX_Creature_AddFeatByLevel(oPC, nFeat, 1); + if (nFeat == PROFICIENCY_ASTROLOGY) + { + NWNX_Creature_SetSkillRank(oPC, SKILL_LORE, iLore +2); + NWNX_Creature_SetSkillRank(oPC, SKILL_SPELLCRAFT, iSpellCraft +2); + } + } + DeleteLocalString(oItem, "ARR_PROF"); + + // Step 7: Age Settings + switch (nAge) + { + case 1: // Middle-Aged + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_STRENGTH, iSTR-1); + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_CONSTITUTION, iCON-1); + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_DEXTERITY, iDEX-1); + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_INTELLIGENCE, iINT+1); + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_CHARISMA, iCHA+1); + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_WISDOM, iWIS+1); + break; + case 2: // Old-Age + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_STRENGTH, iSTR-2); + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_CONSTITUTION, iCON-2); + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_DEXTERITY, iDEX-2); + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_INTELLIGENCE, iINT+2); + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_CHARISMA, iCHA+2); + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_WISDOM, iWIS+2); + break; + case 3: // Venerable + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_STRENGTH, iSTR-3); + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_CONSTITUTION, iCON-3); + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_DEXTERITY, iDEX-3); + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_INTELLIGENCE, iINT+3); + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_CHARISMA, iCHA+3); + NWNX_Creature_SetRawAbilityScore(oPC, ABILITY_WISDOM, iWIS+3); + break; + } + + // Step 8: One-Armed? + if (bDisfigured) + { + NWNX_Creature_AddFeatByLevel(oPC, 2000, 1);//One-Armed + //Set Arm to be nonexistent. I set the non-models to be all model number 200. + + SetCreatureBodyPart(CREATURE_PART_LEFT_BICEP, 200, oPC); + SetCreatureBodyPart(CREATURE_PART_LEFT_FOREARM, 200, oPC); + SetCreatureBodyPart(CREATURE_PART_LEFT_HAND, 200, oPC); + } +} diff --git a/src/nss/bg_apply_cv.nss b/src/nss/bg_apply_cv.nss index 9ca41b3..d7bd40a 100644 --- a/src/nss/bg_apply_cv.nss +++ b/src/nss/bg_apply_cv.nss @@ -8,8 +8,8 @@ #include "nwnx_creature" #include "te_afflic_func" #include "lv_inc" -#include "inc_persist_loca" #include "te_lang" +#include "bg_inc_p_locals" // Ensure the PC Data Object exists; create if missing object EnsurePlayerDataObject(object oPC) @@ -28,7 +28,7 @@ void ApplyProficiencies(object oPC) { object oItem = EnsurePlayerDataObject(oPC); if (!GetIsObjectValid(oItem)) { - SendMessageToPC(oPC, "DEBUG: Invalid player data object"); + //SendMessageToPC(oPC, "DEBUG: Invalid player data object"); return; } @@ -49,7 +49,7 @@ void ApplyProficiencies(object oPC) if (nProficiencyFeat > 0) { - SendMessageToPC(oPC, "DEBUG: Applying feat " + IntToString(nProficiencyFeat) + " from slot " + sSlot); + //SendMessageToPC(oPC, "DEBUG: Applying feat " + IntToString(nProficiencyFeat) + " from slot " + sSlot); NWNX_Creature_AddFeatByLevel(oPC, nProficiencyFeat, 1); nFound++; @@ -63,7 +63,7 @@ void ApplyProficiencies(object oPC) i++; } - SendMessageToPC(oPC, "DEBUG: Applied " + IntToString(nFound) + " proficiencies"); + //SendMessageToPC(oPC, "DEBUG: Applied " + IntToString(nFound) + " proficiencies"); } /* void ApplyProficiencies(object oPC) @@ -103,6 +103,7 @@ void ApplyProficiencies(object oPC) } } */ + void ApplyBonusLanguages(object oPC) { object oItem = GetItemPossessedBy(oPC, "PC_Data_Object"); @@ -162,7 +163,8 @@ void main() int iSpellCraft = GetSkillRank(SKILL_SPELLCRAFT, oPC, TRUE); // Effects effect eSpellResist = EffectSpellResistanceIncrease(11+iCL); - + + //SendMessageToPC(oPC, "DEBUG: bg_apply_cv main() entered"); // Step 0: Subrace/Ethnicity (some of this might be redundant now) if (nSubrace > 0) NWNX_Creature_AddFeatByLevel(oPC, nSubrace, 1); @@ -202,7 +204,7 @@ void main() NWNX_Creature_AddFeatByLevel(oPC, FEAT_LANGUAGE_TALFIRIC, 1); //:: For legacy DMFI - SetLocalInt(oItem, IntToString(LangFeatToLangID(FEAT_LANGUAGE_DAMARAN)), 1); + SetLocalInt(oItem, IntToString(LangFeatToLangID(FEAT_LANGUAGE_TALFIRIC)), 1); } break; case ETHNICITY_ILLUSKAN: @@ -565,7 +567,7 @@ void main() if (nDeity > 0) NWNX_Creature_AddFeat(oPC, nDeity); // Step 5: Languages - ApplyBonusLanguages(oPC); + DelayCommand(0.0f, ApplyBonusLanguages(oPC)); /* string sArr = GetLocalString(oItem, "ARR_LANGUAGES"); int i; for (i = 0; i < ArrayLength(sArr); i++) @@ -576,7 +578,7 @@ void main() DeleteLocalString(oItem, "ARR_LANGUAGES"); */ // Step 6: Proficiencies - ApplyProficiencies(oPC); + DelayCommand(0.0f, ApplyProficiencies(oPC)); /* sArr = GetLocalString(oItem, "ARR_PROF"); for (i = 0; i < ArrayLength(sArr); i++) diff --git a/src/nss/bg_background.nss b/src/nss/bg_background.nss new file mode 100644 index 0000000..9198103 --- /dev/null +++ b/src/nss/bg_background.nss @@ -0,0 +1,3 @@ +void main() +{ +} diff --git a/src/nss/bg_background_cv.nss b/src/nss/bg_background_cv.nss index 750ae74..f08600d 100644 --- a/src/nss/bg_background_cv.nss +++ b/src/nss/bg_background_cv.nss @@ -1,7 +1,7 @@ // bg_background_cv.nss -#include "inc_dynconv" +#include "bg_inc_dynconv" #include "x2_inc_switches" -#include "inc_persist_loca" +#include "bg_inc_p_locals" #include "te_afflic_func" @@ -24,7 +24,8 @@ object EnsurePlayerDataObject(object oPC) WriteTimestampedLogEntry("Language data object recreated"); } return oItem; -} +} + int _CanBeAffluent(object oPC = OBJECT_SELF) { @@ -491,7 +492,7 @@ void main() { object oPC = GetPCSpeaker(); object oItem = EnsurePlayerDataObject(oPC); - SendMessageToPC(oPC, "DEBUG: bg_background_cv main() entered"); + //SendMessageToPC(oPC, "DEBUG: bg_background_cv main() entered"); int nValue = GetLocalInt(oPC, DYNCONV_VARIABLE); int nStage = GetStage(oPC); @@ -595,7 +596,7 @@ void main() // 13 Eldreth Veluuthra if (nRacial == RACIAL_TYPE_ELF) { - AddChoice(" Eldreth Veluuthra", 13, oPC); + AddChoice("Eldreth Veluuthra", 13, oPC); SetLocalString(oPC, "bg_dyn_text_13", "You believe that elves are the superior race, and that they have a right as well as a duty to reclaim the Wealdath forest. You are angered or disgusted by the very existence of half elves and will strive to drive these abominations out of the unspoiled woods that remain..\n\nDoes this describe you?"); } @@ -869,7 +870,8 @@ void main() } else if(nValue == DYNCONV_EXITED) { - DelayCommand(0.1f, StartDynamicConversation("bg_deity_cv", oPC)); + SetPersistantLocalInt(oPC, "Background_Stage", 3); + DelayCommand(0.1f, StartDynamicConversation("bg_deity_cv", oPC, FALSE, FALSE, TRUE, OBJECT_SELF)); } else if(nValue == DYNCONV_ABORTED) { diff --git a/src/nss/bg_deity_cv.nss b/src/nss/bg_deity_cv.nss index e675569..14e7722 100644 --- a/src/nss/bg_deity_cv.nss +++ b/src/nss/bg_deity_cv.nss @@ -1,5 +1,5 @@ // bg_deity_cv.nss -#include "inc_dynconv" +#include "bg_inc_dynconv" #include "bg_inc_p_locals" #include "inc_alignment" #include "te_afflic_func" @@ -42,7 +42,7 @@ object EnsurePlayerDataObject(object oPC) void main() { object oPC = GetPCSpeaker(); - SendMessageToPC(oPC, "DEBUG: bg_deity_cv main() entered"); + //SendMessageToPC(oPC, "DEBUG: bg_deity_cv main() entered"); int nRacialType = GetRacialType(oPC); int nAlignment = GetCreaturesAlignment(oPC); @@ -1317,8 +1317,9 @@ void main() } } AllowExit(DYNCONV_EXIT_FORCE_EXIT, TRUE, oPC); - SetPersistantLocalInt(oPC, "CC3_DONE", 1); - DelayCommand(0.1f, StartDynamicConversation("bg_language_cv", oPC)); + SetPersistantLocalInt(oPC, "CC3_DONE", 1); + SetPersistantLocalInt(oPC, "Background_Stage", 4); + DelayCommand(0.1f, StartDynamicConversation("bg_language_cv", oPC, FALSE, FALSE, TRUE, OBJECT_SELF)); } else { // No diff --git a/src/nss/bg_disfig_cv.nss b/src/nss/bg_disfig_cv.nss index ecb743f..7a17ad5 100644 --- a/src/nss/bg_disfig_cv.nss +++ b/src/nss/bg_disfig_cv.nss @@ -1,22 +1,43 @@ // bg_disfig_cv.nss -#include "inc_dynconv" +#include "bg_inc_dynconv" #include "x2_inc_switches" +#include "bg_inc_p_locals" const int STAGE_LIST = 0; const int STAGE_CONFIRM = 1; + +// Ensure the PC Data Object exists; create if missing +object EnsurePlayerDataObject(object oPC) +{ + object oItem = GetItemPossessedBy(oPC, "PC_Data_Object"); + if (!GetIsObjectValid(oItem)) + { + oItem = CreateItemOnObject("pc_data_object", oPC); + SendMessageToPC(oPC, "Language data object recreated"); + WriteTimestampedLogEntry("Language data object recreated"); + } + return oItem; +} -void main() { +void main() +{ object oPC = GetPCSpeaker(); - SendMessageToPC(oPC, "DEBUG: bg_disfig_cv main() entered"); + //SendMessageToPC(oPC, "DEBUG: bg_disfig_cv main() entered"); + + object oItem = EnsurePlayerDataObject(oPC); + 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) { + if (nValue == DYNCONV_SETUP_STAGE) + { + if (!GetIsStageSetUp(nStage, oPC)) + { + if (nStage == STAGE_LIST) + { SetHeader( "Some players wish to have intentional disfigurements such as lost limbs or lingering injuries. " + "These are rare among those who still wish to adventure, but not unique. Many keep these as " + @@ -31,13 +52,14 @@ void main() { MarkStageSetUp(nStage, oPC); SetDefaultTokens(); } - else if (nStage == STAGE_CONFIRM) { + else if (nStage == STAGE_CONFIRM) + { int nSelected = GetLocalInt(oPC, "disfig_selected"); string sPrompt = (nSelected == 1) ? "You lost an arm. At some point, you lost one. Maybe it was a woodcutting accident, " + "maybe it was a punishment for theft. In any case, you're down an appendage.\n\n" + - "You can't use your left arm. This means one-handed weapons only, no two handers. " + - "No left hand rings.\n\nAre you sure?" + "You can't use your left arm. This means one-handed weapons only, no shields, " + + "no left hand rings.\n\nAre you sure?" : "Are you sure you want to continue without a disfigurement?"; SetHeader(sPrompt); AddChoice("Yes!", nSelected, oPC); @@ -48,19 +70,40 @@ void main() { SetupTokens(); } } - else { + else if (nValue == DYNCONV_EXITED) + { + SetPersistantLocalInt(oPC, "Background_Stage", 8); + DelayCommand(0.1f, ExecuteScript("bg_apply_cv", oPC)); + } + else + { int nChoice = GetChoice(oPC); - if (nStage == STAGE_LIST) { + if (nStage == STAGE_LIST) + { SetLocalInt(oPC, "disfig_selected", nChoice); SetStage(STAGE_CONFIRM, oPC); } - else if (nStage == STAGE_CONFIRM) { - if (nChoice >= 0) { - string sGrant = (nChoice == 1) ? "bg_give_noarm" : "bg_give_nodisfig"; - ExecuteScript(sGrant, oPC); + else if (nStage == STAGE_CONFIRM) + { + if (nChoice >= 0) + { + //string sGrant = (nChoice == 1) ? "bg_give_noarm" : "bg_give_nodisfig"; + //ExecuteScript(sGrant, oPC); // Chain to next step, e.g., final touches - // StartDynamicConversation("bg_final_cv", oPC); - } else { + if (nChoice == 1) + { + SetLocalInt(oItem,"CC7",1); // One-Armed + SetPersistantLocalInt(oPC,"CC7",1); // One-Armed + SetLocalInt(oItem,"BG_Select",8); + } + else + { + SetLocalInt(oItem,"BG_Select",8); + } + AllowExit(DYNCONV_EXIT_FORCE_EXIT, TRUE, oPC); + } + else + { SetStage(STAGE_LIST, oPC); } } diff --git a/src/nss/bg_inc_array.nss b/src/nss/bg_inc_array.nss new file mode 100644 index 0000000..97eb9a0 --- /dev/null +++ b/src/nss/bg_inc_array.nss @@ -0,0 +1,568 @@ +//:://///////////////////////////////////////////// +//:: Array simulation include +//:: bg_inc_array +//::////////////////////////////////////////////// +/** @file + Array simulation include + + This file defines a set of functions for creating + and manipulating arrays, which are implemented as + local variables on some holder object. + + + Notes: + + * Arrays are dynamic and may be increased in size by just _set_'ing a new + element + * There are no restrictions on what is in the array (can have multiple + types in the same array) + * Arrays start at index 0 + + //////////////////////////////////////////////////////////////////////////////// + // (c) Mr. Figglesworth 2002 + // This code is licensed under beerware. You are allowed to freely use it + // and modify it in any way. Your only two obligations are: (1) at your option, + // to buy the author a beer if you ever meet him; and (2) include the + // copyright notice and license in any redistribution of this code or + // alterations of it. + // + // Full credit for how the array gets implemented goes to the guy who wrote + // the article and posted it on the NWNVault (I couldn't find your article + // to give you credit :( ). And, of course, to bioware. Great job! + //////////////////////////////////////////////////////////////////////////////// +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +/** + * Creates a new array on the given storage object. If an + * array with the same name already exists, the function + * errors. + * + * @param store The object to use as holder for the array + * @param name The name of the array + * @return SDL_SUCCESS if the array was successfully created, + * one of SDL_ERROR_* on error. + */ +int array_create(object store, string name); + +/** + * Deletes an array, erasing all it's entries. + * + * @param store The object which holds the array to delete + * @param name The name of the array + * @return SDL_SUCCESS if the array was successfully deleted, + * one of SDL_ERROR_* on error + */ +int array_delete(object store, string name); + +/** + * Stores a string in an array. + * + * @param store The object holding the array + * @param name The name of the array + * @param i The index to store the string at + * @param entry The string to store + * @return SDL_SUCCESS if the store was successfull, SDL_ERROR_* on error. + */ +int array_set_string(object store, string name, int i, string entry); + +/** + * Stores an integer in an array. + * + * @param store The object holding the array + * @param name The name of the array + * @param i The index to store the integer at + * @param entry The integer to store + * @return SDL_SUCCESS if the store was successfull, SDL_ERROR_* on error. + */ +int array_set_int(object store, string name, int i, int entry); + +/** + * Stores a float in an array. + * + * @param store The object holding the array + * @param name The name of the array + * @param i The index to store the float at + * @param entry The float to store + * @return SDL_SUCCESS if the store was successfull, SDL_ERROR_* on error. + */ +int array_set_float(object store, string name, int i, float entry); + +/** + * Stores an object in an array. + * + * @param store The object holding the array + * @param name The name of the array + * @param i The index to store the object at + * @param entry The object to store + * @return SDL_SUCCESS if the store was successfull, SDL_ERROR_* on error. + */ +int array_set_object(object store, string name, int i, object entry); + +/** + * Gets a string from an array. + * + * @param store The object holding the array + * @param name The name of the array + * @param i The index to retrieve the string from + * @return The value contained at the index on success, + * "" on error + */ +string array_get_string(object store, string name, int i); + +/** + * Gets an integer from an array. + * + * @param store The object holding the array + * @param name The name of the array + * @param i The index to retrieve the integer from + * @return The value contained at the index on success, + * 0 on error + */ +int array_get_int(object store, string name, int i); + +/** + * Gets a float from an array. + * + * @param store The object holding the array + * @param name The name of the array + * @param i The index to retrieve the float from + * @return The value contained at the index on success, + * 0.0f on error + */ +float array_get_float(object store, string name, int i); + +/** + * Gets an object from an array. + * + * @param store The object holding the array + * @param name The name of the array + * @param i The index to retrieve the object from + * @return The value contained at the index on success, + * OBJECT_INVALID on error + */ +object array_get_object(object store, string name, int i); + +/** + * Removes all entries in the array with indexes greater than or equal to + * the new size and sets the array size to be equal to the new size. + * + * @param store The object holding the array + * @param name The name of the array + * @param size_new The new number of entries in the array + * @return SDL_SUCCESS on successful resize, SDL_ERROR_* on + * error + */ +int array_shrink(object store, string name, int size_new); + +/** + * Gets the current size of the array. This is one greater + * than the index of highest indexed element the array + * has contained since the last array_shrink or the new size + * specified by the last array_shrink, whichever is greater. + * + * @param store The object holding the array + * @param name The name of the array + * @return The size of the array, or -1 if the specified + * array does not exist. + */ +int array_get_size(object store, string name); + +/** + * Checks whether the given array exists. + * + * @param store The object holding the array + * @param name The name of the array + * @return TRUE if the array exists, FALSE otherwise. + */ +int array_exists(object store, string name); + +/* These need to be rewritten and made less bug-prone before being taken into use. + Preferrably not necessarily have it be fucking massive square matrix, but instead + store a separate length for each x row. + +int array_2d_create(object store, string name); +int array_2d_delete(object store, string name); + +int array_2d_set_string(object store, string name, int i, int j, string entry); +int array_2d_set_int(object store, string name, int i, int j, int entry); +int array_2d_set_float(object store, string name, int i, int j, float entry); +int array_2d_set_object(object store, string name, int i, int j, object entry); + +// returns "" or 0 on error +string array_2d_get_string(object store, string name, int i, int j); +int array_2d_get_int(object store, string name, int i, int j); +float array_2d_get_float(object store, string name, int i, int j); +object array_2d_get_object(object store, string name, int i, int j); + +// changes memory usage of array (deletes x[ > size_new]) +int array_2d_shrink(object store, string name, int size_new, int axis); + +// gets current maximum size of array +int array_2d_get_size(object store, string name, int axis); + +int array_2d_exists(object store, string name); +*/ + +///////////////////////////////////// +// Error Returns +///////////////////////////////////// + +const int SDL_SUCCESS = 1; +const int SDL_ERROR = 1000; +const int SDL_ERROR_ALREADY_EXISTS = 1001; +const int SDL_ERROR_DOES_NOT_EXIST = 1002; +const int SDL_ERROR_OUT_OF_BOUNDS = 1003; +const int SDL_ERROR_NO_ZERO_SIZE = 1004; // Not used - Ornedan 2006.09.15 +const int SDL_ERROR_NOT_VALID_OBJECT = 1005; +const int SDL_ERROR_INVALID_PARAMETER = 1006; + + +///////////////////////////////////// +// Implementation +///////////////////////////////////// + +int array_create(object store, string name) +{ + // error checking + if(!GetIsObjectValid(store)) + return SDL_ERROR_NOT_VALID_OBJECT; + else if(array_exists(store, name)) + return SDL_ERROR_ALREADY_EXISTS; + else + { + // Initialize the size (always one greater than the actual size) + SetLocalInt(store,name,1); + return SDL_SUCCESS; + } +} + +void array_delete_loop(object store, string name, int nMin, int nMax) +{ + int i = nMin; + while(i < nMin + 250 && i < nMax) + { + DeleteLocalString(store, name+"_"+IntToString(i)); + + // just in case, delete possible object names + DeleteLocalObject(store, name+"_"+IntToString(i)+"_OBJECT"); + i++; + } + // delay continuation to avoid TMI + if(i < nMax) + DelayCommand(0.0, array_delete_loop(store, name, i, nMax)); +} + +int array_delete(object store, string name) +{ + // error checking + int size=GetLocalInt(store,name); + if (size==0) + return SDL_ERROR_DOES_NOT_EXIST; + + array_delete_loop(store, name, 0, size+5); + + DeleteLocalInt(store,name); + + return SDL_SUCCESS; +} + +int array_set_string(object store, string name, int i, string entry) +{ + int size = GetLocalInt(store,name); + if(size == 0) + return SDL_ERROR_DOES_NOT_EXIST; + if(i < 0) + return SDL_ERROR_OUT_OF_BOUNDS; + + SetLocalString(store,name+"_"+IntToString(i),entry); + + // save size if we've enlarged it + if(i+2>size) + SetLocalInt(store,name,i+2); + + return SDL_SUCCESS; +} + +int array_set_int(object store, string name, int i, int entry) +{ + return array_set_string(store,name,i,IntToString(entry)); +} + +int array_set_float(object store, string name, int i, float entry) +{ + return array_set_string(store,name,i,FloatToString(entry)); +} + +int array_set_object(object store, string name, int i, object entry) +{ + int results = array_set_string(store, name, i, "OBJECT"); + if (results == SDL_SUCCESS) + SetLocalObject(store, name + "_" + IntToString(i) + "_OBJECT", entry); + + return results; +} + +string array_get_string(object store, string name, int i) +{ + // error checking + int size=GetLocalInt(store,name); + if (size==0 || i>size || i < 0) + return ""; + + return GetLocalString(store,name+"_"+IntToString(i)); +} + +int array_get_int(object store, string name, int i) +{ + return StringToInt(array_get_string(store,name,i)); +} + +float array_get_float(object store, string name, int i) +{ + return StringToFloat(array_get_string(store,name,i)); +} + +object array_get_object(object store, string name, int i) +{ + if(array_get_string(store, name, i) == "OBJECT") + return GetLocalObject(store,name+"_"+IntToString(i)+"_OBJECT"); + else + return OBJECT_INVALID; +} + +int array_shrink(object store, string name, int size_new) +{ + // Get the current size value + int size = GetLocalInt(store, name); + // Error check - non-existent array + if(size == 0) + return SDL_ERROR_DOES_NOT_EXIST; + // If the new number of elements is equal to or greater than the current number of elements, autosuccess + if((size - 1) <= size_new) + return SDL_SUCCESS; + + // Delete entries that are outside the new array bounds + int i; + for(i = size_new; i < size; i++) + { + DeleteLocalString(store, name+"_"+IntToString(i)); + + // just in case, delete possible object names + DeleteLocalObject(store, name+"_"+IntToString(i)+"_OBJECT"); + } + + // Store the new size, with the +1 existence marker + SetLocalInt(store, name, size_new + 1); + + return SDL_SUCCESS; +} + +int array_get_size(object store, string name) +{ + return GetLocalInt(store, name) - 1; +} + +int array_exists(object store, string name) +{ + // If the size and presence indicator local is non-zero, the array exists. Normalize it's value to TRUE / FALSE and return + return GetLocalInt(store, name) != 0; +} + +/* +int array_2d_create(object store, string name) +{ + // error checking + if(!GetIsObjectValid(store)) + return SDL_ERROR_NOT_VALID_OBJECT; + else if(GetLocalInt(store,name)) + return SDL_ERROR_ALREADY_EXISTS; + else + { + // Initialize the size (always one greater than the actual size) + SetLocalInt(store,name+"_A",1); + SetLocalInt(store,name+"_B",1); + return SDL_SUCCESS; + } +} + + +int array_2d_delete(object store, string name) +{ + // error checking + int sizeA=GetLocalInt(store,name+"_A"); + if (sizeA==0) + return SDL_ERROR_DOES_NOT_EXIST; + int sizeB=GetLocalInt(store,name+"_B"); + if (sizeB==0) + return SDL_ERROR_DOES_NOT_EXIST; + + int i; + int j; + for (i=0; isizeA) + SetLocalInt(store,name+"_A",i+2); + if (j+2>sizeB) + SetLocalInt(store,name+"_B",j+2); + + return SDL_SUCCESS; +} + + +int array_2d_set_int(object store, string name, int i, int j, int entry) +{ + return array_2d_set_string(store,name,i,j,IntToString(entry)); +} + +int array_2d_set_float(object store, string name, int i, int j, float entry) +{ + return array_2d_set_string(store,name,i,j,FloatToString(entry)); +} + +int array_2d_set_object(object store, string name, int i, int j, object entry) +{ + // object is a little more complicated. + // we want to create an object as a local variable too + if (!GetIsObjectValid(entry)) + return SDL_ERROR_NOT_VALID_OBJECT; + + int results=array_2d_set_string(store,name,i,j,"OBJECT"); + if (results==SDL_SUCCESS) + SetLocalObject(store,name+"_"+IntToString(i)+"_"+IntToString(j)+"_OBJECT",entry); + + return results; +} + + +string array_2d_get_string(object store, string name, int i, int j) +{ + int sizeA=GetLocalInt(store,name+"_A"); + if (sizeA==0 || i>sizeA) + return ""; + int sizeB=GetLocalInt(store,name+"_B"); + if (sizeB==0 || j>sizeB) + return ""; + + return GetLocalString(store,name+"_"+IntToString(i)+"_"+IntToString(j)); +} + +int array_2d_get_int(object store, string name, int i, int j) +{ + return StringToInt(array_2d_get_string(store,name,i,j)); +} + +float array_2d_get_float(object store, string name, int i, int j) +{ + return StringToFloat(array_2d_get_string(store,name,i,j)); +} + +object array_2d_get_object(object store, string name, int i, int j) +{ + return GetLocalObject(store,name+"_"+IntToString(i)+"_"+IntToString(j)+"_OBJECT"); +} + + +int array_2d_shrink(object store, string name, int size_new, int axis) +{ + // error checking + int sizeA=GetLocalInt(store,name+"_A"); + if (sizeA==0) + return SDL_ERROR_DOES_NOT_EXIST; + int sizeB=GetLocalInt(store,name+"_B"); + if (sizeB==0) + return SDL_ERROR_DOES_NOT_EXIST; + + if (axis == 1 && + (sizeA==size_new || sizeA" + sString + ""); + if(oAdditionalRecipient != OBJECT_INVALID) + SendMessageToPC(oAdditionalRecipient, "" + sString + ""); + WriteTimestampedLogEntry(sString); +} + +void Assert(int bAssertion, string sAssertion, string sMessage = "", string sFileName = "", string sFunction = "") +{ + if(bAssertion == FALSE) + { + //SpawnScriptDebugger(); + string sErr = "Assertion failed: " + sAssertion; + + if(sMessage != "" || sFileName != "" || sFunction != "") + { + sErr += "\n"; + + if(sMessage != "") + sErr += sMessage; + + if(sFileName != "" || sFunction != "") + { + if(sMessage != "") + sErr += "\n"; + + sErr += "At " + sFileName; + + if(sFileName != "" && sFunction != "") + sErr += ": "; + + sErr += sFunction; + } + } + + DoDebug(sErr); + Die(); + } +} + +void Die() +{ + while(TRUE) {;} +} + +string DebugObject2Str(object o) +{ + return o == OBJECT_INVALID ? + "OBJECT_INVALID" : // Special case + "'" + GetName(o) + "' - '" + GetTag(o) + "' - '" + GetResRef(o) + "' - " + ObjectToString(o); +} + +string DebugLocation2Str(location loc) +{ + object oArea = GetAreaFromLocation(loc); + vector vPos = GetPositionFromLocation(loc); + string sX, sY, sZ, sF; + // 3 decimal places and no leading whitespace + sX = FloatToString(vPos.x,0,3); + sY = FloatToString(vPos.y,0,3); + sZ = FloatToString(vPos.z,0,3); + sF = FloatToString(GetFacingFromLocation(loc),0,3); + + return "Area: Name = '" + GetName(oArea) + "', Tag = '" + GetTag(oArea) + "'; Position: (" + sX + ", " + sY + ", " + sZ + ",); Facing: " + sF; +} + +string DebugIProp2Str(itemproperty iprop) +{ + return "Type: " + IntToString(GetItemPropertyType(iprop)) + "; " + + "Subtype: " + IntToString(GetItemPropertySubType(iprop)) + "; " + + "Duration type: " + (GetItemPropertyDurationType(iprop) == DURATION_TYPE_INSTANT ? "DURATION_TYPE_INSTANT" : + GetItemPropertyDurationType(iprop) == DURATION_TYPE_TEMPORARY ? "DURATION_TYPE_TEMPORARY" : + GetItemPropertyDurationType(iprop) == DURATION_TYPE_PERMANENT ? "DURATION_TYPE_PERMANENT" : + IntToString(GetItemPropertyDurationType(iprop))) + "; " + + "Param1: " + IntToString(GetItemPropertyParam1(iprop)) + "; " + + "Param1 value: " + IntToString(GetItemPropertyParam1Value(iprop)) + "; " + + "Cost table: " + IntToString(GetItemPropertyCostTable(iprop)) + "; " + + "Cost table value: " + IntToString(GetItemPropertyCostTableValue(iprop)); +} + +string DebugBool2String(int bool) +{ + return bool ? "True" : "False"; +} + +string DebugEffect2String(effect eEffect) +{ + return "Effect; Type = " + IntToString(GetEffectType(eEffect)) + + ", SpellID: " + IntToString(GetEffectSpellId(eEffect)) + + ", Subtype: " + IntToString(GetEffectSubType(eEffect)) + + ", Duration: " + IntToString(GetEffectDurationType(eEffect)) + + ", Creator: " + GetName(GetEffectCreator(eEffect)); +} diff --git a/src/nss/bg_inc_dynconv.nss b/src/nss/bg_inc_dynconv.nss new file mode 100644 index 0000000..99c3921 --- /dev/null +++ b/src/nss/bg_inc_dynconv.nss @@ -0,0 +1,729 @@ +//::////////////////////////////////////////////// +//:: Dynamic Conversation System include +//:: bg_inc_dynconv +//::////////////////////////////////////////////// +/** @file + + + + @author Primogenitor + @date 2005.09.23 - Rebuilt the system - Ornedan +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +//PRC8 Token pre-fix = 161838 + +////////////////////////////////////////////////// +/* Constant definitions */ +////////////////////////////////////////////////// + +const int DYNCONV_EXITED = -2; +const int DYNCONV_ABORTED = -3; +const int DYNCONV_SETUP_STAGE = -1; + +const int DYNCONV_TOKEN_HEADER = 16183899; +const int DYNCONV_TOKEN_REPLY_0 = 161838100; +const int DYNCONV_TOKEN_REPLY_1 = 161838101; +const int DYNCONV_TOKEN_REPLY_2 = 161838102; +const int DYNCONV_TOKEN_REPLY_3 = 161838103; +const int DYNCONV_TOKEN_REPLY_4 = 161838104; +const int DYNCONV_TOKEN_REPLY_5 = 161838105; +const int DYNCONV_TOKEN_REPLY_6 = 161838106; +const int DYNCONV_TOKEN_REPLY_7 = 161838107; +const int DYNCONV_TOKEN_REPLY_8 = 161838108; +const int DYNCONV_TOKEN_REPLY_9 = 161838109; +const int DYNCONV_TOKEN_EXIT = 161838110; +const int DYNCONV_TOKEN_WAIT = 161838111; +const int DYNCONV_TOKEN_NEXT = 161838112; +const int DYNCONV_TOKEN_PREV = 161838113; +const int DYNCONV_MIN_TOKEN = 16183899; +const int DYNCONV_MAX_TOKEN = 161838113; + +const int DYNCONV_STRREF_PLEASE_WAIT = 16824202; // "Please wait" +const int DYNCONV_STRREF_PREVIOUS = 16824203; // "Previous" +const int DYNCONV_STRREF_NEXT = 16824204; // "Next" +const int DYNCONV_STRREF_ABORT_CONVO = 16824212; // "Abort" +const int DYNCONV_STRREF_EXIT_CONVO = 78; // "Exit" + +const string DYNCONV_SCRIPT = "DynConv_Script"; +const string DYNCONV_VARIABLE = "DynConv_Var"; +const string DYNCONV_STAGE = "DynConv_Stage"; +const string DYNCONV_TOKEN_BASE = "DynConv_TOKEN"; +const string DYNCONV_CHOICEOFFSET = "ChoiceOffset"; + +/** + * Exiting the conversation is not allowed. The exit + * choice is not shown + */ +const int DYNCONV_EXIT_NOT_ALLOWED = 0; +/** + * Exiting the conversation is allowed and it is + * forced to exit due to no nodes being shown. + */ +const int DYNCONV_EXIT_FORCE_EXIT = -1; +/** + * Exiting the conversation is allowed and the exit + * choice is shown. + */ +const int DYNCONV_EXIT_ALLOWED_SHOW_CHOICE = 1; + + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Sets the header token and reply tokens for the PC to values stored + * via SetHeader and AddChoice, respectively. + * + * @param oPC The PC involved in the conversation. If left + * to default, GetPCSpeaker is used + */ +void SetupTokens(object oPC = OBJECT_INVALID); + +/** + * Builds the local variable name for a token. + * + * @param nTokenID One of the DYNCONV_TOKEN_* constants + */ +string GetTokenIDString(int nTokenID); + +/** + * Sets the dynamic conversation header. ie, the "NPC"'s reply. + * + * @param sText The text to set the header to + * @param oPC The PC involved in the conversation. If left + * to default, GetPCSpeaker is used + */ +void SetHeader(string sText, object oPC = OBJECT_INVALID); + +/** + * A wrapper for SetHeader() that uses TLK references. + * + * @param nStrRef The TLK entry to use + * @param oPC The PC involved in the conversation. If left + * to default, GetPCSpeaker is used + */ +void SetHeaderStrRef(int nStrRef, object oPC = OBJECT_INVALID); + +/** + * Add a reply choice to be displayed. The replies are displayed in + * the same order as they are added. + * + * @param sText The text of the choice + * @param nValue The numeric value of the choice. This is what will be + * returned by GetChoice() + * @param oPC The PC involved in the conversation. If left + * to default, GetPCSpeaker is used + */ +void AddChoice(string sText, int nValue, object oPC = OBJECT_INVALID); + +/** + * A wrapper for AddChoice() that uses TLK references. + * + * @param nStrRef The TLK entry to use + * @param nValue The numeric value of the choice. This is what will be + * returned by GetChoice() + * @param oPC The PC involved in the conversation. If left + * to default, GetPCSpeaker is used + */ +void AddChoiceStrRef(int nStrRef, int nValue, object oPC = OBJECT_INVALID); + +/** + * Sets the custom token at nTokenID to be the given string and stores + * the value in a local variable on OBJECT_SELF. + * Used by the dyynamic onversation system to track token assignment. + * + * @param nTokenID The custom token number to store the string in + * @param sString The string to store + * @param oPC The PC whose conversation this token belongs to + */ +void SetToken(int nTokenID, string sString, object oPC = OBJECT_SELF); + +/** + * Sets the default values for the Exit, Wait, Next and Previous + * tokens. The values will be as follows, or their translated + * equivalents should a non-english TLK be used. + * + * Exit = "Exit" + * Wait = "Please wait" + * Next = "Next" + * Previous = "Previous" + */ +void SetDefaultTokens(); + +/** + * Changes the conversation stage. If the new stage given is + * the same as the current, nothing happens. Otherwise + * the stage is changed and the choices stored for the old + * stage are deleted. + * + * @param nNewStage The stage to enter + * @param oPC The PC involved in the conversation. If left + * to default, GetPCSpeaker is used + */ +void SetStage(int nNewStage, object oPC = OBJECT_INVALID); + +/** + * Gets the current stage of the conversation. + * + * @param oPC The PC involved in the conversation. If left + * to default, GetPCSpeaker is used + * @return The current stage of the conversation, as previously + * set via SetStage() or 0 if no calls to SetStage() + * have been done yet. + */ +int GetStage(object oPC = OBJECT_INVALID); + +/** + * Gets the value of the choice selected by the PC. + * + * @param oPC The PC involved in the conversation. If left + * to default, GetPCSpeaker is used + * @return The value of the choice the PC made, as set + * by a call to AddChoice(). + */ +int GetChoice(object oPC = OBJECT_INVALID); + +/** + * Gets the text of the choice selected by the PC. + * + * @param oPC The PC involved in the conversation. If left + * to default, GetPCSpeaker is used + * @return The text of the choice the PC made, as set + * by a call to AddChoice(). + */ +string GetChoiceText(object oPC = OBJECT_INVALID); + +/** + * Starts a dynamic conversation. Results are unspecified if called while + * already inside a dynamic conversation. + * + * @param sConversationScript The script to use for controlling the conversation. + * @param oPC The PC that is to be doing the responding in the conversation. + * @param bAllowExit One of the DYNCONV_EXIT_* constants. + * @param bAllowAbort If TRUE, the PC is allowed to aborts the conversation by moving / doing + * some other action or being involved in combat. This can be changed later + * on using AllowAbort() + * @param bForceStart If TRUE, the PC's actions are cleared, so the conversation starts immediately + * and cannot be avoided by the PC cancelling the action while it is in the queue. + * @param oConverseWith The object to speak the "NPC" side of the conversation. Usually, this is also + * the PC, which will be used when this parameter is left to it's default value. + * NOTE: If this parameter is given a value other than OBJECT_INVALID, no validity + * testing is performed upon that object. The function caller needs to make sure + * the object exists. + */ +void StartDynamicConversation(string sConversationScript, object oPC, + int nAllowExit = DYNCONV_EXIT_ALLOWED_SHOW_CHOICE, int bAllowAbort = FALSE, + int bForceStart = FALSE, object oConverseWith = OBJECT_INVALID); + +/** + * Starts using another dynamic conversation script while inside a + * dynamic conversation. Should only be called from a dynamic conversation + * script. + * The current conversation's script and allow exit/abort variables are + * saved. When the conversation entered via this call exits, the system + * returns to the current conversation, with stage being the one specified + * in the call to this function. + * NOTE: Any stage setup markers are not stored for the return. + * + * @param sConversationToEnter The conversation script to use in the branch + * @param nStageToReturnTo The value of stage variable upong return + * from the branch. + * @param bAllowExit The branch's initial exit allowance state. See + * StartDynamicConversation() for more details. + * @param bAllowAbort The branch's initial abort allowance state. See + * StartDynamicConversation() for more details. + * @param oPC The PC involved in the conversation. If left + * to default, GetPCSpeaker is used. + */ +void BranchDynamicConversation(string sConversationToEnter, int nStageToReturnTo, + int bAllowExit = TRUE, int bAllowAbort = FALSE, + object oPC = OBJECT_INVALID); + +/** + * Marks the current dynconvo as exitable via the exit conversation + * choice. + * + * @param nNewValue One of the DYNCONV_EXIT_* constants + * @param bChangeExitTokenText If this is TRUE, then changes the text on + * DYNCONV_TOKEN_EXIT to "Exit" + * @param oPC The PC involved in the conversation. If left + * to default, GetPCSpeaker is used. + */ +void AllowExit(int nNewValue = DYNCONV_EXIT_ALLOWED_SHOW_CHOICE, int bChangeExitTokenText = TRUE, object oPC = OBJECT_INVALID); + +/** + * Marks the conversation as abortable, meaning that the player is + * allowed to leave the conversation via means other than the + * exit conversation choice. + * + * @param oPC The PC involved in the conversation. If left to + * default, GetPCSpeaker is used. + */ +void AllowAbort(object oPC = OBJECT_INVALID); + +/** + * Checks whether the given stage is marked as already set up. + * + * @param nStage The stage to check + * @param oPC The PC involved in the conversation. If left to + * default, GetPCSpeaker is used. + */ +int GetIsStageSetUp(int nStage, object oPC = OBJECT_INVALID); + +/** + * Marks a stage as being set up. This means that when + * the conversation script is called to set up the + * stage, nothing is done and old values are used instead. + * This is useful for scrolling lists, as CPU is not + * wasted on rebuilding the exact same list. + * + * @param nStage The stage to set marker for + * @param oPC The PC involved in the conversation. If left to + * default, GetPCSpeaker is used. + */ +void MarkStageSetUp(int nStage, object oPC = OBJECT_INVALID); + +/** + * Marks the stage as not set up. This is used to undo + * the effects of MarkStageSetUp() when there is + * need to rerun the stage's builder. + * An example of such situation would be returning to + * a stage from another. + * + * @param nStage The stage to unset marker for + * @param oPC The PC involved in the conversation. If left to + * default, GetPCSpeaker is used. + */ +void MarkStageNotSetUp(int nStage, object oPC = OBJECT_INVALID); + +/** + * Clears the current stage's choices and marks it not set up. + * + * @param oPC The PC involved in the conversation. If left to + * default, GetPCSpeaker is used. + */ +void ClearCurrentStage(object oPC = OBJECT_INVALID); + + + +////////////////////////////////////////////////// +/* Include section */ +////////////////////////////////////////////////// + +#include "bg_inc_array" +#include "bg_inc_debug" + +////////////////////////////////////////////////// +/* Internal function prototypes */ +////////////////////////////////////////////////// + +void _DynConvInternal_ExitedConvo(object oPC, int bAbort); +void _DynConvInternal_RunScript(object oPC, int nDynConvVar); +void _DynConvInternal_PreScript(object oPC); +void _DynConvInternal_PostScript(object oPC); +object _DynConvInternal_ResolvePC(object oPC); + + +////////////////////////////////////////////////// +/* Function Definitions */ +////////////////////////////////////////////////// + +void SetupTokens(object oPC = OBJECT_INVALID) +{ + oPC = _DynConvInternal_ResolvePC(oPC); + // Set header + SetCustomToken(DYNCONV_TOKEN_HEADER, GetLocalString(oPC, "DynConv_HeaderText")); + + // Set reply tokens. Assumes that the tokens used are a continuous block. + int nOffset = GetLocalInt(oPC, DYNCONV_CHOICEOFFSET); + int i; + for (i = 0; i < 10; i++) + { + SetToken(DYNCONV_TOKEN_REPLY_0 + i, array_get_string(oPC, "ChoiceTokens", nOffset + i), oPC); + } +} + +void SetHeader(string sText, object oPC = OBJECT_INVALID) +{ + oPC = _DynConvInternal_ResolvePC(oPC); + SetLocalString(oPC, "DynConv_HeaderText", sText); +} + +void SetHeaderStrRef(int nStrRef, object oPC = OBJECT_INVALID) +{ + SetHeader(GetStringByStrRef(nStrRef), oPC); +} + +string GetTokenIDString(int nTokenID) +{ + return DYNCONV_TOKEN_BASE + IntToString(nTokenID); +} + +void AddChoice(string sText, int nValue, object oPC = OBJECT_INVALID) +{ + oPC = _DynConvInternal_ResolvePC(oPC); + if(!array_exists(oPC, "ChoiceTokens")) + array_create(oPC, "ChoiceTokens"); + if(!array_exists(oPC, "ChoiceValues")) + array_create(oPC, "ChoiceValues"); + array_set_string(oPC, "ChoiceTokens", array_get_size(oPC, "ChoiceTokens"), sText); + array_set_int (oPC, "ChoiceValues", array_get_size(oPC, "ChoiceValues"), nValue); +} + +void AddChoiceStrRef(int nStrRef, int nValue, object oPC = OBJECT_INVALID) +{ + AddChoice(GetStringByStrRef(nStrRef), nValue, oPC); +} + +void SetToken(int nTokenID, string sString, object oPC = OBJECT_SELF) +{ + // Set the token + SetCustomToken(nTokenID, sString); + // Set a marker on the PC for the reply conditional scripts to check + SetLocalString(oPC, GetTokenIDString(nTokenID), sString); +} + +string GetToken(int nTokenID, object oPC = OBJECT_SELF) +{ + // Set a marker on the PC for the reply conditional scripts to check + return GetLocalString(oPC, GetTokenIDString(nTokenID)); +} + +void SetDefaultTokens() +{ + SetCustomToken(DYNCONV_TOKEN_EXIT, GetStringByStrRef(DYNCONV_STRREF_EXIT_CONVO)); + SetCustomToken(DYNCONV_TOKEN_WAIT, GetStringByStrRef(DYNCONV_STRREF_PLEASE_WAIT)); + SetCustomToken(DYNCONV_TOKEN_NEXT, GetStringByStrRef(DYNCONV_STRREF_NEXT)); + SetCustomToken(DYNCONV_TOKEN_PREV, GetStringByStrRef(DYNCONV_STRREF_PREVIOUS)); +} + +void _DynConvInternal_ExitedConvo(object oPC, int bAbort) +{ + // Restart convo if not allowed to leave yet + if(bAbort && !GetLocalInt(oPC, "DynConv_AllowAbort")) // Allowed to abort? + { + if(DEBUG) DoDebug("_DynConvInternal_ExitedConvo(): Conversation aborted, restarting."); + AssignCommand(oPC, ClearAllActions(TRUE)); + AssignCommand(oPC, ActionStartConversation(oPC, "dyncov_base", TRUE, FALSE)); + SetLocalInt(oPC, "DynConv_RestartMarker", TRUE); + } + // Allowed to exit? Technically, the only way this branch should ever be run is by there not being any response choices available + else if(!bAbort && + (GetLocalInt(oPC, "DynConv_AllowExit") == DYNCONV_EXIT_NOT_ALLOWED)) + { + if(DEBUG) DoDebug("_DynConvInternal_ExitedConvo(): ERROR: Conversation exited via exit node while exiting not allowed!\n" + + "DYNCONV_SCRIPT = '" + GetLocalString(oPC, DYNCONV_SCRIPT) + "'\n" + ); + + AssignCommand(oPC, ClearAllActions(TRUE)); + AssignCommand(oPC, ActionStartConversation(oPC, "dyncov_base", TRUE, FALSE)); + SetLocalInt(oPC, "DynConv_RestartMarker", TRUE); + } + else{ + // Run the conversation script's exit handler + SetLocalInt(oPC, DYNCONV_VARIABLE, bAbort ? DYNCONV_ABORTED : DYNCONV_EXITED); + ExecuteScript(GetLocalString(oPC, DYNCONV_SCRIPT), OBJECT_SELF); + + // If there are entries remaining in the stack, pop the previous conversation + if(GetLocalInt(oPC, "DynConv_Stack")) + { + if(DEBUG) DoDebug("_DynConvInternal_ExitedConvo(): Exited a branch"); + // Clean up after the previous conversation + array_delete(oPC, "ChoiceTokens"); + array_delete(oPC, "ChoiceValues"); + array_delete(oPC, "StagesSetup"); + DeleteLocalInt(oPC, "ChoiceOffset"); + + // Pop the data from the stack + int nStack = GetLocalInt(oPC, "DynConv_Stack"); + int nStage = GetLocalInt(oPC, "DynConv_Stack_ReturnToStage_" + IntToString(nStack)); + int nAllowExit = GetLocalInt(oPC, "DynConv_Stack_AllowExit_" + IntToString(nStack)); + int nAllowAbort = GetLocalInt(oPC, "DynConv_Stack_AllowAbort_" + IntToString(nStack)); + string sScript = GetLocalString(oPC, "DynConv_Stack_Script_" + IntToString(nStack)); + + // Delete the stack level + DeleteLocalInt(oPC, "DynConv_Stack_ReturnToStage_" + IntToString(nStack)); + DeleteLocalInt(oPC, "DynConv_Stack_AllowExit_" + IntToString(nStack)); + DeleteLocalInt(oPC, "DynConv_Stack_AllowAbort_" + IntToString(nStack)); + DeleteLocalString(oPC, "DynConv_Stack_Script_" + IntToString(nStack)); + if(nStack - 1 > 0) SetLocalInt(oPC, "DynConv_Stack", nStack - 1); + else DeleteLocalInt(oPC, "DynConv_Stack"); + + // Store the date in the conversation variables + SetLocalInt(oPC, DYNCONV_STAGE, nStage); + SetLocalInt(oPC, "DynConv_AllowExit", nAllowExit); + SetLocalInt(oPC, "DynConv_AllowAbort", nAllowAbort); + SetLocalString(oPC, DYNCONV_SCRIPT, sScript); + + // Restart the conversation + AssignCommand(oPC, ClearAllActions(TRUE)); + AssignCommand(oPC, ActionStartConversation(oPC, "dyncov_base", TRUE, FALSE)); + } + // Fully exited the conversation. Clean up + else + { + if(DEBUG) DoDebug("_DynConvInternal_ExitedConvo(): Fully exited conversation"); + array_delete(oPC, "ChoiceTokens"); + array_delete(oPC, "ChoiceValues"); + array_delete(oPC, "StagesSetup"); + + DeleteLocalInt(oPC, "ChoiceOffset"); + DeleteLocalInt(oPC, "DynConv_AllowExit"); + DeleteLocalInt(oPC, "DynConv_AllowAbort"); + + DeleteLocalInt(oPC, DYNCONV_VARIABLE); + DeleteLocalInt(oPC, DYNCONV_STAGE); + DeleteLocalString(oPC, DYNCONV_SCRIPT); + DeleteLocalString(oPC, "DynConv_HeaderText"); + + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_HEADER)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_REPLY_0)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_REPLY_1)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_REPLY_2)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_REPLY_3)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_REPLY_4)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_REPLY_5)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_REPLY_6)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_REPLY_7)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_REPLY_8)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_REPLY_9)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_EXIT)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_WAIT)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_NEXT)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_PREV)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_MIN_TOKEN)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_MAX_TOKEN)); + + //int i; + //for(i = DYNCONV_MIN_TOKEN; i <= DYNCONV_MAX_TOKEN; i++) + //DeleteLocalString(oPC, GetTokenIDString(i)); + } + } +} + +void _DynConvInternal_RunScript(object oPC, int nDynConvVar) +{ + if(!GetLocalInt(oPC, "DynConv_RestartMarker")) + { + _DynConvInternal_PreScript(oPC); + string sScript = GetLocalString(oPC, DYNCONV_SCRIPT); + SetLocalInt(oPC, DYNCONV_VARIABLE, nDynConvVar); + ExecuteScript(sScript, OBJECT_SELF); + _DynConvInternal_PostScript(oPC); + } + else + { + SetupTokens(oPC); + DeleteLocalInt(oPC, "DynConv_RestartMarker"); + } +} + +void _DynConvInternal_PreScript(object oPC) +{ + // Create the choice arrays + array_create(oPC, "ChoiceTokens"); + array_create(oPC, "ChoiceValues"); +} + +void _DynConvInternal_PostScript(object oPC) +{ + // If debugging is active, check that the conversations have at least one response node + // when exiting is off + if(DEBUG) + { + if(GetLocalInt(oPC, DYNCONV_VARIABLE) == DYNCONV_SETUP_STAGE && + GetLocalInt(oPC, "DynConv_AllowExit") == DYNCONV_EXIT_NOT_ALLOWED && + array_get_size(oPC, "ChoiceTokens") == 0 + ) + { + DoDebug("Dynconvo ERROR: No response tokens set up and exiting not allowed!"); + } + } +} + +object _DynConvInternal_ResolvePC(object oPC) +{ + return oPC == OBJECT_INVALID ? GetPCSpeaker() : oPC; // If no valid PC reference was passed, get it via GetPCSpeaker +} + +void SetStage(int nNewStage, object oPC = OBJECT_INVALID) +{ + oPC = _DynConvInternal_ResolvePC(oPC); + // No need to act if the stage wasn't changed + if(nNewStage != GetStage(oPC)) + { + + SetLocalInt(oPC, DYNCONV_STAGE, nNewStage); + + // Clear the choice data + array_delete(oPC, "ChoiceTokens"); + array_delete(oPC, "ChoiceValues"); + DeleteLocalInt(oPC, "ChoiceOffset"); + } +} + +int GetStage(object oPC = OBJECT_INVALID) +{ + oPC = _DynConvInternal_ResolvePC(oPC); + return GetLocalInt(oPC, DYNCONV_STAGE); +} + +int GetChoice(object oPC = OBJECT_INVALID) +{ + oPC = _DynConvInternal_ResolvePC(oPC); + return array_get_int(oPC, "ChoiceValues", GetLocalInt(oPC, DYNCONV_VARIABLE) // Number of choice + - 1 // Which begins at index 1 instead of the index 0 we need here + + GetLocalInt(oPC, "ChoiceOffset")); +} + +string GetChoiceText(object oPC = OBJECT_INVALID) +{ + oPC = _DynConvInternal_ResolvePC(oPC); + return array_get_string(oPC, "ChoiceTokens", GetLocalInt(oPC, DYNCONV_VARIABLE) // Number of choice + - 1 // Which begins at index 1 instead of the index 0 we need here + + GetLocalInt(oPC, "ChoiceOffset")); +} + +void StartDynamicConversation(string sConversationScript, object oPC, + int nAllowExit = DYNCONV_EXIT_ALLOWED_SHOW_CHOICE, int bAllowAbort = FALSE, + int bForceStart = FALSE, object oConverseWith = OBJECT_INVALID) +{ + if(IsInConversation(oPC)) + { + if(DEBUG) DoDebug("StartDynamicConversation(): Aborting--already in conversation"); + return; + } + + if(DEBUG) DoDebug("StartDynamicConversation(): Starting new dynamic conversation, parameters:\n" + + "sConversationScript = '" + sConversationScript + "'\n" + + "oPC = " + DebugObject2Str(oPC) + "\n" + + "nAllowExit = " + (nAllowExit == DYNCONV_EXIT_NOT_ALLOWED ? "DYNCONV_EXIT_NOT_ALLOWED" : + nAllowExit == DYNCONV_EXIT_FORCE_EXIT ? "DYNCONV_EXIT_FORCE_EXIT" : + nAllowExit == DYNCONV_EXIT_ALLOWED_SHOW_CHOICE ? "DYNCONV_EXIT_ALLOWED_SHOW_CHOICE" : + "ERROR: Unsupported value: " + IntToString(nAllowExit) + ) + "\n" + + "bAllowAbort = " + DebugBool2String(bAllowAbort) + "\n" + + "bForceStart = " + DebugBool2String(bForceStart) + "\n" + + "oConverseWith = " + DebugObject2Str(oConverseWith) + "\n" + ); + // By default, the PC converses with itself + oConverseWith = oConverseWith == OBJECT_INVALID ? oPC : oConverseWith; + if(DEBUG) if(!GetIsObjectValid(oConverseWith)) DoDebug("StartDynamicConversation(): ERROR: oConverseWith is not valid!"); + + // Store the exit control variables + SetLocalInt(oPC, "DynConv_AllowExit", nAllowExit); + SetLocalInt(oPC, "DynConv_AllowAbort", bAllowAbort); + + // Initiate conversation + if(bForceStart) AssignCommand(oPC, ClearAllActions(TRUE)); + SetLocalString(oPC, DYNCONV_SCRIPT, sConversationScript); + AssignCommand(oPC, ActionStartConversation(oConverseWith, "dyncov_base", TRUE, FALSE)); +} + +void BranchDynamicConversation(string sConversationToEnter, int nStageToReturnTo, + int nAllowExit = DYNCONV_EXIT_ALLOWED_SHOW_CHOICE, int bAllowAbort = FALSE, + object oPC = OBJECT_INVALID) +{ + if(DEBUG) DoDebug("BranchDynamicConversation(): Entering another dynamic conversation, parameters:\n" + + "sConversationToEnter = '" + sConversationToEnter + "'\n" + + "nStageToReturnTo = " + IntToString(nStageToReturnTo) + "\n" + + "nAllowExit = " + (nAllowExit == DYNCONV_EXIT_NOT_ALLOWED ? "DYNCONV_EXIT_NOT_ALLOWED" : + nAllowExit == DYNCONV_EXIT_FORCE_EXIT ? "DYNCONV_EXIT_FORCE_EXIT" : + nAllowExit == DYNCONV_EXIT_ALLOWED_SHOW_CHOICE ? "DYNCONV_EXIT_ALLOWED_SHOW_CHOICE" : + "ERROR: Unsupported value: " + IntToString(nAllowExit) + ) + "\n" + + "bAllowAbort = " + DebugBool2String(bAllowAbort) + "\n" + + "oPC = " + DebugObject2Str(oPC) + "\n " + ); + oPC = _DynConvInternal_ResolvePC(oPC); + // Get current stack level + int nStack = GetLocalInt(oPC, "DynConv_Stack") + 1; + + // Push the return data onto the stack + SetLocalInt(oPC, "DynConv_Stack_ReturnToStage_" + IntToString(nStack), nStageToReturnTo); + SetLocalInt(oPC, "DynConv_Stack_AllowExit_" + IntToString(nStack), + GetLocalInt(oPC, "DynConv_AllowExit")); + SetLocalInt(oPC, "DynConv_Stack_AllowAbort_" + IntToString(nStack), + GetLocalInt(oPC, "DynConv_AllowAbort")); + SetLocalString(oPC, "DynConv_Stack_Script_" + IntToString(nStack), + GetLocalString(oPC, DYNCONV_SCRIPT)); + SetLocalInt(oPC, "DynConv_Stack", nStack); + + // Clean the current conversation data + array_delete(oPC, "ChoiceTokens"); + array_delete(oPC, "ChoiceValues"); + array_delete(oPC, "StagesSetup"); + DeleteLocalInt(oPC, "ChoiceOffset"); + DeleteLocalInt(oPC, DYNCONV_STAGE); + + // Set the new conversation as active + SetLocalString(oPC, DYNCONV_SCRIPT, sConversationToEnter); + SetLocalInt(oPC, "DynConv_AllowExit", nAllowExit); + SetLocalInt(oPC, "DynConv_AllowAbort", bAllowAbort); +} + +/// @todo Rename to SetExitable +void AllowExit(int nNewValue = DYNCONV_EXIT_ALLOWED_SHOW_CHOICE, int bChangeExitTokenText = TRUE, object oPC = OBJECT_INVALID) +{ + if(DEBUG) DoDebug("AllowExit():\n" + + "nNewValue = " + (nNewValue == DYNCONV_EXIT_NOT_ALLOWED ? "DYNCONV_EXIT_NOT_ALLOWED" : + nNewValue == DYNCONV_EXIT_FORCE_EXIT ? "DYNCONV_EXIT_FORCE_EXIT" : + nNewValue == DYNCONV_EXIT_ALLOWED_SHOW_CHOICE ? "DYNCONV_EXIT_ALLOWED_SHOW_CHOICE" : + "ERROR: Unsupported value: " + IntToString(nNewValue) + ) + "\n" + + "bChangeExitTokenText = " + DebugBool2String(bChangeExitTokenText) + "\n" + + "oPC = " + DebugObject2Str(_DynConvInternal_ResolvePC(oPC)) + "\n" + ); + + SetLocalInt(_DynConvInternal_ResolvePC(oPC), "DynConv_AllowExit", nNewValue); + if(bChangeExitTokenText) + SetCustomToken(DYNCONV_TOKEN_EXIT, GetStringByStrRef(DYNCONV_STRREF_EXIT_CONVO)); +} + +/// @todo Replace with SetAbortable(int bAllow, object oPC = OBJECT_INVALID) +void AllowAbort(object oPC = OBJECT_INVALID) +{ + SetLocalInt(_DynConvInternal_ResolvePC(oPC), "DynConv_AllowAbort", TRUE); +} + +int GetIsStageSetUp(int nStage, object oPC = OBJECT_INVALID) +{ + oPC = _DynConvInternal_ResolvePC(oPC); + + if(!array_exists(oPC, "StagesSetup")) + return FALSE; + return array_get_int(oPC, "StagesSetup", nStage); +} + +void MarkStageSetUp(int nStage, object oPC = OBJECT_INVALID) +{ + oPC = _DynConvInternal_ResolvePC(oPC); + + if(!array_exists(oPC, "StagesSetup")) + array_create(oPC, "StagesSetup"); + array_set_int(oPC, "StagesSetup", nStage, TRUE); +} + +void MarkStageNotSetUp(int nStage, object oPC = OBJECT_INVALID) +{ + oPC = _DynConvInternal_ResolvePC(oPC); + + if(!array_exists(oPC, "StagesSetup")) + return; + array_set_int(oPC, "StagesSetup", nStage, FALSE); +} + +void ClearCurrentStage(object oPC = OBJECT_INVALID) +{ + oPC = _DynConvInternal_ResolvePC(oPC); + + // Clear the choice data + array_delete(oPC, "ChoiceTokens"); + array_delete(oPC, "ChoiceValues"); + DeleteLocalInt(oPC, "ChoiceOffset"); + + MarkStageNotSetUp(GetStage(oPC), oPC); +} diff --git a/src/nss/bg_inc_p_locals.nss b/src/nss/bg_inc_p_locals.nss new file mode 100644 index 0000000..fd237b2 --- /dev/null +++ b/src/nss/bg_inc_p_locals.nss @@ -0,0 +1,436 @@ +//:://///////////////////////////////////////////// +//:: Persistant local variables include +//:: bg_inc_p_locals +//::////////////////////////////////////////////// +/** @file + A set of functions for storing local variables + on a token item stored in the creature's skin. + Since local variables on items within containers + are not stripped during serialization, these + variables persist across module changes and + server resets. + + These functions work on NPCs in addition to PCs, + but the persitence is mostly useless for them, + since NPCs are usually not serialized in a way + that would remove normal locals from them, if + they are serialized at all. +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// +//:: Adapted by ebonfowl to fix the Beamdog local variable bug +//:: Functions all still intuitively work the same way +//:: Only difference is the variables all run through SQL rather than via the hide token +//:: Dedicated to Edgar, the real Ebonfowl +//::////////////////////////////////////////////// + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Gets the token item inside the given creature's hide, on which the persistant + * variables are stored on. + * If a token does not exist already, one is created. + * WARNING: If called on a non-creature object, returns the object itself. + * + * @param oPC The creature whose storage token to get + * @param bAMS - TRUE will return special token for alternate magic system buckup info + * @return The creature's storage token + * + * GetNSBToken - special token for New Spellbook System information + */ +//object GetHideToken(object oPC, int bAMS = FALSE); + +/** + * Set a local string variable on the creature's storage token. + * + * @param oPC The creature whose local variables to manipulate + * @param sName The name of the local variable to manipulate + * @param sValue The value to set the string local to + */ +void SetPersistantLocalString(object oPC, string sName, string sValue); + +/** + * Set a local integer variable on the creature's storage token. + * + * @param oPC The creature whose local variables to manipulate + * @param sName The name of the local variable to manipulate + * @param nValue The value to set the integer local to + */ +void SetPersistantLocalInt(object oPC, string sName, int nValue); + +/** + * Set a local float variable on the creature's storage token. + * + * @param oPC The creature whose local variables to manipulate + * @param sName The name of the local variable to manipulate + * @param nValue The value to set the float local to + */ +void SetPersistantLocalFloat(object oPC, string sName, float fValue); + +/** + * Set a local location variable on the creature's storage token. + * + * CAUTION! See note in SetPersistantLocalObject(). Location also contains an + * object reference, though it will only break across changes to the module, + * not across server resets. + * + * @param oPC The creature whose local variables to manipulate + * @param sName The name of the local variable to manipulate + * @param nValue The value to set the location local to + */ +void SetPersistantLocalLocation(object oPC, string sName, location lValue); + +/** + * Set a local object variable on the creature's storage token. + * + * CAUTION! Object references are likely (and in some cases, certain) to break + * when transferring across modules or upon server reset. This means that + * persistantly stored local objects may not refer to the same object after such + * a change, if they refer to a valid object at all. + * + * @param oPC The creature whose local variables to manipulate + * @param sName The name of the local variable to manipulate + * @param nValue The value to set the object local to + */ +void SetPersistantLocalObject(object oPC, string sName, object oValue); + +/** + * Get a local string variable from the creature's storage token. + * + * @param oPC The creature whose local variables to manipulate + * @param sName The name of the local variable to manipulate + * @return The string local specified. On error, returns "" + */ +string GetPersistantLocalString(object oPC, string sName); + +/** + * Get a local integer variable from the creature's storage token. + * + * @param oPC The creature whose local variables to manipulate + * @param sName The name of the local variable to manipulate + * @return The integer local specified. On error, returns 0 + */ +int GetPersistantLocalInt(object oPC, string sName); + +/** + * Get a local float variable from the creature's storage token. + * + * @param oPC The creature whose local variables to manipulate + * @param sName The name of the local variable to manipulate + * @return The float local specified. On error, returns 0.0 + */ +float GetPersistantLocalFloat(object oPC, string sName); + +/** + * Get a local location variable from the creature's storage token. + * + * CAUTION! See note in SetPersistantLocalLocation() + * + * @param oPC The creature whose local variables to manipulate + * @param sName The name of the local variable to manipulate + * @return The location local specified. Return value on error is unknown + */ +location GetPersistantLocalLocation(object oPC, string sName); + +/** + * Get a local object variable from the creature's storage token. + * + * CAUTION! See note in SetPersistantLocalObject() + * + * @param oPC The creature whose local variables to manipulate + * @param sName The name of the local variable to manipulate + * @return The object local specified. On error, returns OBJECT_INVALID + */ +object GetPersistantLocalObject(object oPC, string sName); + +/** + * Delete a local string variable on the creature's storage token. + * + * @param oPC The creature whose local variables to manipulate + * @param sName The name of the local variable to manipulate + */ +void DeletePersistantLocalString(object oPC, string sName); + +/** + * Delete a local integer variable on the creature's storage token. + * + * @param oPC The creature whose local variables to manipulate + * @param sName The name of the local variable to manipulate + */ +void DeletePersistantLocalInt(object oPC, string sName); + +/** + * Delete a local float variable on the creature's storage token. + * + * @param oPC The creature whose local variables to manipulate + * @param sName The name of the local variable to manipulate + */ +void DeletePersistantLocalFloat(object oPC, string sName); + +/** + * Delete a local location variable on the creature's storage token. + * + * @param oPC The creature whose local variables to manipulate + * @param sName The name of the local variable to manipulate + */ +void DeletePersistantLocalLocation(object oPC, string sName); + +/** + * Delete a local object variable on the creature's storage token. + * + * @param oPC The creature whose local variables to manipulate + * @param sName The name of the local variable to manipulate + */ +void DeletePersistantLocalObject(object oPC, string sName); + + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "bg_inc_skin" + +// SQL include +#include "inc_persistsql" + + +////////////////////////////////////////////////// +/* Function defintions */ +////////////////////////////////////////////////// + +object GetHideToken(object oPC, int bAMS = FALSE) +{ + string sCache = bAMS ? "PRC_AMSTokenCache" : "PRC_HideTokenCache"; + string sTag = bAMS ? "AMS_Token" : "HideToken"; + + // Creatureness check - non-creatures don't get persistent storage from here + if(!(GetObjectType(oPC) == OBJECT_TYPE_CREATURE)) + return oPC; // Just return a reference to the object itself + + object oHide = GetPCSkin(oPC); + object oToken = GetLocalObject(oPC, sCache); + + if(!GetIsObjectValid(oToken)) + { + object oTest = GetFirstItemInInventory(oHide); + while(GetIsObjectValid(oTest)) + { + if(GetTag(oTest) == sTag) + { + oToken = oTest; + break;//exit while loop + } + oTest = GetNextItemInInventory(oHide); + } + if(!GetIsObjectValid(oToken)) + { + oToken = GetItemPossessedBy(oPC, sTag); + + // Move the token to hide's inventory + if(GetIsObjectValid(oToken)) + AssignCommand(oHide, ActionTakeItem(oToken, oPC)); // Does this work? - Ornedan + else + { + //oToken = CreateItemOnObject("hidetoken", oPC); + //AssignCommand(oHide, ActionTakeItem(oToken, oPC)); + oToken = CreateItemOnObject("hidetoken", oHide, 1, sTag); + } + } + + AssignCommand(oToken, SetIsDestroyable(FALSE)); + // Cache the token so that there needn't be multiple loops over an inventory + SetLocalObject(oPC, sCache, oToken); + //- If the cache reference is found to break under any conditions, uncomment this. + //looks like logging off then back on without the server rebooting breaks it + //I guess because the token gets a new ID, but the local still points to the old one + //Ive changed it to delete the local in OnClientEnter. Primogenitor + //DelayCommand(1.0f, DeleteLocalObject(oPC, "PRC_HideTokenCache")); + } + return oToken; +} + +void SetPersistantLocalString(object oPC, string sName, string sValue) +{ + if(GetIsPC(oPC) == TRUE && GetMaster(oPC) == OBJECT_INVALID && !GetIsDMPossessed(oPC)) + { + SQLocalsPlayer_SetString(oPC, sName, sValue); + } + else + { + SetLocalString(oPC, sName, sValue); + } +} + +/* void SetPersistantLocalString(object oPC, string sName, string sValue) +{ + if(GetIsPC(oPC)) + { + SQLocalsPlayer_SetString(oPC, sName, sValue); + } + else + { + SetLocalString(oPC, sName, sValue); + } +} */ + +void SetPersistantLocalInt(object oPC, string sName, int nValue) +{ + if(GetIsPC(oPC) == TRUE && GetMaster(oPC) == OBJECT_INVALID && !GetIsDMPossessed(oPC)) + { + SQLocalsPlayer_SetInt(oPC, sName, nValue); + } + else + { + SetLocalInt(oPC, sName, nValue); + } +} + +void SetPersistantLocalFloat(object oPC, string sName, float fValue) +{ + if(GetIsPC(oPC) == TRUE && GetMaster(oPC) == OBJECT_INVALID && !GetIsDMPossessed(oPC)) + { + SQLocalsPlayer_SetFloat(oPC, sName, fValue); + } + else + { + SetLocalFloat(oPC, sName, fValue); + } +} + +void SetPersistantLocalLocation(object oPC, string sName, location lValue) +{ + if(GetIsPC(oPC) == TRUE && GetMaster(oPC) == OBJECT_INVALID && !GetIsDMPossessed(oPC)) + { + SQLocalsPlayer_SetLocation(oPC, sName, lValue); + } + else + { + SetLocalLocation(oPC, sName, lValue); + } +} + +void SetPersistantLocalObject(object oPC, string sName, object oValue) +{ + if(GetIsPC(oPC) == TRUE && GetMaster(oPC) == OBJECT_INVALID && !GetIsDMPossessed(oPC)) + { + SQLocalsPlayer_SetObject(oPC, sName, oValue); + } + else + { + SetLocalObject(oPC, sName, oValue); + } +} + +string GetPersistantLocalString(object oPC, string sName) +{ + if(GetIsPC(oPC) == TRUE && GetMaster(oPC) == OBJECT_INVALID && !GetIsDMPossessed(oPC)) + { + return SQLocalsPlayer_GetString(oPC, sName); + } + return GetLocalString(oPC, sName); +} + +int GetPersistantLocalInt(object oPC, string sName) +{ + if(GetIsPC(oPC) == TRUE && GetMaster(oPC) == OBJECT_INVALID && !GetIsDMPossessed(oPC)) + { + return SQLocalsPlayer_GetInt(oPC, sName); + } + return GetLocalInt(oPC, sName); +} + +float GetPersistantLocalFloat(object oPC, string sName) +{ + if(GetIsPC(oPC) == TRUE && GetMaster(oPC) == OBJECT_INVALID && !GetIsDMPossessed(oPC)) + { + return SQLocalsPlayer_GetFloat(oPC, sName); + } + return GetLocalFloat(oPC, sName); +} + +location GetPersistantLocalLocation(object oPC, string sName) +{ + if(GetIsPC(oPC) == TRUE && GetMaster(oPC) == OBJECT_INVALID && !GetIsDMPossessed(oPC)) + { + return SQLocalsPlayer_GetLocation(oPC, sName); + } + return GetLocalLocation(oPC, sName); +} + +object GetPersistantLocalObject(object oPC, string sName) +{ + if(GetIsPC(oPC) == TRUE && GetMaster(oPC) == OBJECT_INVALID && !GetIsDMPossessed(oPC)) + { + // Additional check since the OID returned may be invalid, but not actually OBJECT_INVALID + object oReturn = SQLocalsPlayer_GetObject(oPC, sName); + if(GetIsObjectValid(oReturn)) return oReturn; + return OBJECT_INVALID; + } + return GetLocalObject(oPC, sName); +} + +void DeletePersistantLocalString(object oPC, string sName) +{ + if(GetIsPC(oPC) == TRUE && GetMaster(oPC) == OBJECT_INVALID && !GetIsDMPossessed(oPC)) + { + SQLocalsPlayer_DeleteString(oPC, sName); + } + else + { + DeleteLocalString(oPC, sName); + } +} + +void DeletePersistantLocalInt(object oPC, string sName) +{ + if(GetIsPC(oPC) == TRUE && GetMaster(oPC) == OBJECT_INVALID && !GetIsDMPossessed(oPC)) + { + SQLocalsPlayer_DeleteInt(oPC, sName); + } + else + { + DeleteLocalInt(oPC, sName); + } +} + +void DeletePersistantLocalFloat(object oPC, string sName) +{ + if(GetIsPC(oPC) == TRUE && GetMaster(oPC) == OBJECT_INVALID && !GetIsDMPossessed(oPC)) + { + SQLocalsPlayer_DeleteFloat(oPC, sName); + } + else + { + DeleteLocalFloat(oPC, sName); + } +} + +void DeletePersistantLocalLocation(object oPC, string sName) +{ + if(GetIsPC(oPC) == TRUE && GetMaster(oPC) == OBJECT_INVALID && !GetIsDMPossessed(oPC)) + { + SQLocalsPlayer_DeleteLocation(oPC, sName); + } + else + { + DeleteLocalLocation(oPC, sName); + } +} + +void DeletePersistantLocalObject(object oPC, string sName) +{ + if(GetIsPC(oPC) == TRUE && GetMaster(oPC) == OBJECT_INVALID && !GetIsDMPossessed(oPC)) + { + SQLocalsPlayer_DeleteObject(oPC, sName); + } + else + { + DeleteLocalObject(oPC, sName); + } +} + +// Test main + +// void main() {} \ No newline at end of file diff --git a/src/nss/bg_inc_skin.nss b/src/nss/bg_inc_skin.nss new file mode 100644 index 0000000..d91ca25 --- /dev/null +++ b/src/nss/bg_inc_skin.nss @@ -0,0 +1,139 @@ +//:://///////////////////////////////////////////// +//:: skin include +//:: prc_inc_skin +//:://///////////////////////////////////////////// +/** @file + This include contains GetPCSkin(). If only using + this function please include this directly and not via + the entire spell engine :p + + Is included by bg_inc_p_locals for persistent + local variables +*/ +//::////////////////////////////////////////////// +//:: Created By: fluffyamoeba +//:: Created On: 2008-4-23 +//::////////////////////////////////////////////// + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Sets up the pcskin object on oPC. + * If it already exists, simply return it. Otherwise, create and equip it. + * + * @param oPC The creature whose skin object to look for. + * @return Either the skin found or the skin created. + */ +object GetPCSkin(object oPC); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "bg_inc_debug" + +////////////////////////////////////////////////// +/* private functions */ +////////////////////////////////////////////////// +// to duplicate ForceEquip() from inc_utility + +void _ForceEquipSkin(object oPC, object oSkin, int nThCall = 0) +{ + // Make sure that the object we are attempting equipping is the latest one to be ForceEquipped into this slot + if(GetIsObjectValid(GetLocalObject(oPC, "ForceEquipToSlot_17")) + && GetLocalObject(oPC, "ForceEquipToSlot_17") != oSkin) + return; + + // Fail on non-commandable NPCs after ~1min + if(!GetIsPC(oPC) && !GetCommandable(oPC) && nThCall > 60) + { + WriteTimestampedLogEntry("ForceEquip() failed on non-commandable NPC: " + DebugObject2Str(oPC) + " for item: " + DebugObject2Str(oSkin)); + return; + } + + float fDelay; + + // Check if the equipping has already happened + if(GetItemInSlot(INVENTORY_SLOT_CARMOUR, oPC) != oSkin) + { + // Test and increment the control counter + if(nThCall++ == 0) + { + // First, try to do the equipping non-intrusively and give the target a reasonable amount of time to do it + AssignCommand(oPC, ActionEquipItem(oSkin, INVENTORY_SLOT_CARMOUR)); + fDelay = 1.0f; + + // Store the item to be equipped in a local variable to prevent contest between two different calls to ForceEquip + SetLocalObject(oPC, "ForceEquipToSlot_17", oSkin); + } + else + { + // Nuke the target's action queue. This should result in "immediate" equipping of the item + if(GetIsPC(oPC) || nThCall > 5) // Skip nuking NPC action queue at first, since that can cause problems. 5 = magic number here. May need adjustment + { + if(!GetIsObjectValid(oSkin)) + { + oSkin = GetPCSkin(oPC); + return; + } + else + { + AssignCommand(oPC, ClearAllActions()); + AssignCommand(oPC, ActionEquipItem(oSkin, INVENTORY_SLOT_CARMOUR)); + } + } + // Use a lenghtening delay in order to attempt handling lag and possible other interference. From 0.1s to 1s + fDelay = (nThCall < 10 ? nThCall : 10) / 10.0f; // yes this is the same as PRCMin(nThCall, 10) + } + + // Loop + DelayCommand(fDelay, _ForceEquipSkin(oPC, oSkin, nThCall)); + } + // It has, so clean up + else + DeleteLocalObject(oPC, "ForceEquipToSlot_17"); +} + +////////////////////////////////////////////////// +/* Function defintions */ +////////////////////////////////////////////////// + +object GetPCSkin(object oPC) +{ + // According to a bug report, this is being called on non-creature objects. This should catch the culprit + if(DEBUG) Assert(GetObjectType(oPC) == OBJECT_TYPE_CREATURE, "GetObjectType(oPC) == OBJECT_TYPE_CREATURE", "GetPRCSkin() called on non-creature object: " + DebugObject2Str(oPC), "prc_inc_skin", "object GetPCSkin(object oPC)"); + object oSkin = GetItemInSlot(INVENTORY_SLOT_CARMOUR, oPC); + if(!GetIsObjectValid(oSkin)) + { + //oSkin = GetLocalObject(oPC, "PRCSkinCache"); + //if(!GetIsObjectValid(oSkin)) + { + //Added this check to prevent creation of extra skins on module entry + oSkin = GetItemPossessedBy(oPC, "base_prc_skin"); + if(GetIsObjectValid(oSkin)) + { + _ForceEquipSkin(oPC, oSkin, INVENTORY_SLOT_CARMOUR); + //AssignCommand(oPC, ActionEquipItem(oSkin, INVENTORY_SLOT_CARMOUR)); + } + else + { + oSkin = CreateItemOnObject("base_prc_skin", oPC); + _ForceEquipSkin(oPC, oSkin, INVENTORY_SLOT_CARMOUR); + //AssignCommand(oPC, ActionEquipItem(oSkin, INVENTORY_SLOT_CARMOUR)); + + // The skin should not be droppable + SetDroppableFlag(oSkin, FALSE); + // other scripts should not be able to destroy the skin + AssignCommand(oSkin, SetIsDestroyable(FALSE)); + } + + // Cache the skin reference for further lookups during the same script + //SetLocalObject(oPC, "PRCSkinCache", oSkin); + //DelayCommand(0.0f, DeleteLocalObject(oPC, "PRCSkinCache")); + } + } + return oSkin; +} + diff --git a/src/nss/bg_language_cv.nss b/src/nss/bg_language_cv.nss index e5eb45f..63d9fee 100644 --- a/src/nss/bg_language_cv.nss +++ b/src/nss/bg_language_cv.nss @@ -1,8 +1,9 @@ // bg_language_cv.nss -#include "inc_dynconv" +#include "bg_inc_dynconv" #include "x2_inc_switches" -#include "inc_persist_loca" +#include "bg_inc_p_locals" #include "te_afflic_func" +#include "te_lang" // Ensure the PC Data Object exists; create if missing object EnsurePlayerDataObject(object oPC) @@ -88,15 +89,14 @@ int GetNextLanguageSlot(object oPC) { string sSlot = "LANGUAGE_" + (nSlot < 10 ? "0" : "") + IntToString(nSlot); SetLocalInt(oItem, sSlot, nLanguageFeat); - SetPersistantLocalInt(oPC, sSlot, nLanguageFeat); + SetPersistantLocalInt(oPC, sSlot, nLanguageFeat); } // mark as selected for the conversation list SetLocalInt(oPC, IntToString(nLanguageFeat), TRUE); } - -// Count automatic racial and class languages using slot-searching pattern + // Count automatic racial and class languages using slot-searching pattern int GetAutomaticLanguageCount(object oPC) { int nAutomaticLanguages = 0; @@ -348,7 +348,7 @@ void main() { // Grant automatic languages before starting conversation GrantDefaultLanguages(oPC); - SendMessageToPC(oPC, "DEBUG: bg_language_cv main() entered"); + //SendMessageToPC(oPC, "DEBUG: bg_language_cv main() entered"); int nValue = GetLocalInt(oPC, DYNCONV_VARIABLE); int nStage = GetStage(oPC); @@ -369,7 +369,8 @@ void main() { if (nValue == DYNCONV_SETUP_STAGE) { if (!GetIsStageSetUp(nStage, oPC)) { - if (nStage == STAGE_LIST) { + if (nStage == STAGE_LIST) + { // Initialize language count if not set if (!GetLocalInt(oPC, "LANGUAGE_COUNT")) { SetLocalInt(oPC, "LANGUAGE_COUNT", 0); @@ -458,7 +459,7 @@ void main() { AddChoice("Lantanese", 24, oPC); } if (nRemaining > 0 && !KnowsLanguage(oPC, FEAT_LANGUAGE_MAZTILAN)) { - AddChoice("Mulanese", 25, oPC); + AddChoice("Maztilan", 25, oPC); } if (nRemaining > 0 && !KnowsLanguage(oPC, FEAT_LANGUAGE_MULANESE)) { AddChoice("Mulanese", 26, oPC); @@ -488,11 +489,19 @@ void main() { AddChoice("Undercommon", 34, oPC); } - // Show Done option if at least one language selected - if (nLangCount > 0) { +/* // Show Done option if at least one language selected + if (nLangCount > 0) + { AddChoice("Done - I have selected enough languages", 99, oPC); SetLocalString(oPC, "lang_dyn_text_99", "Finish language selection and continue to the next step."); - } + } */ + + // Show Done option if at least one language selected OR no picks remaining + if (nLangCount > 0 || nRemaining == 0) + { + AddChoice("Done - I have selected enough languages", 99, oPC); + SetLocalString(oPC, "lang_dyn_text_99", "Finish language selection and continue to the next step."); + } MarkStageSetUp(nStage, oPC); SetDefaultTokens(); @@ -513,7 +522,14 @@ void main() { } } SetupTokens(); - } else { + } + else if (nValue == DYNCONV_EXITED) + { + SetPersistantLocalInt(oPC, "Background_Stage", 5); + DelayCommand(0.1f, StartDynamicConversation("bg_profs_cv", oPC, FALSE, FALSE, TRUE, OBJECT_SELF)); + } + else + { // Handle PC responses int nChoice = GetChoice(oPC); @@ -522,8 +538,11 @@ void main() { if (nChoice == 99) { // Done - finalize and proceed to next step - SetPersistantLocalInt(oPC, "CC4_DONE", 1); - DelayCommand(0.1f, StartDynamicConversation("bg_profs_cv", oPC)); + SetPersistantLocalInt(oPC, "CC4_DONE", 1); + SetLocalInt(oItem, "CC4_DONE", 1); + SetPersistantLocalInt(oPC, "Background_Stage", 5); + DelayCommand(0.1f, StartDynamicConversation("bg_profs_cv", oPC, FALSE, FALSE, TRUE, OBJECT_SELF)); + AllowExit(DYNCONV_EXIT_FORCE_EXIT, TRUE, oPC); } else if (nChoice >= 1 && nChoice <= 34) { @@ -644,8 +663,10 @@ void main() { SetStage(STAGE_CONFIRM, oPC); } } - else if (nStage == STAGE_CONFIRM) { - if (nChoice == CHOICE_CONFIRM_YES) { + else if (nStage == STAGE_CONFIRM) + { + if (nChoice == CHOICE_CONFIRM_YES) + { // Confirm selection - grant language string sLangVar = GetLocalString(oPC, "TEMP_LANG_VAR"); int nLanguageFeat = GetLocalInt(oPC, "TEMP_LANG_FEAT"); @@ -670,10 +691,12 @@ void main() { int nIntMod = GetAbilityModifier(ABILITY_INTELLIGENCE, oPC); if (nIntMod <= 0) nIntMod = 0; - if (nCount >= nIntMod) { + if (nCount >= nIntMod) + { SetPersistantLocalInt(oPC, "CC4_DONE", 1); + SetPersistantLocalInt(oPC, "Background_Stage", 5); + DelayCommand(0.1f, StartDynamicConversation("bg_profs_cv", oPC, FALSE, FALSE, TRUE, OBJECT_SELF)); AllowExit(DYNCONV_EXIT_FORCE_EXIT, TRUE, oPC); - DelayCommand(0.1f, StartDynamicConversation("bg_profs_cv", oPC)); } else { diff --git a/src/nss/bg_profs_cv.nss b/src/nss/bg_profs_cv.nss index 01041ee..195e6d5 100644 --- a/src/nss/bg_profs_cv.nss +++ b/src/nss/bg_profs_cv.nss @@ -1,7 +1,7 @@ // bg_proficiency_cv.nss -#include "inc_dynconv" +#include "bg_inc_dynconv" #include "x2_inc_switches" -#include "inc_persist_loca" +#include "bg_inc_p_locals" #include "te_afflic_func" // Ensure the PC Data Object exists; create if missing @@ -264,7 +264,7 @@ const int CHOICE_CONFIRM_NO = 101; void main() { object oPC = GetPCSpeaker(); - SendMessageToPC(oPC, "DEBUG: bg_prof_cv main() entered"); + //SendMessageToPC(oPC, "DEBUG: bg_prof_cv main() entered"); object oItem = EnsurePlayerDataObject(oPC); @@ -398,7 +398,14 @@ void main() { } } SetupTokens(); - } else { + } + else if (nValue == DYNCONV_EXITED) + { + SetPersistantLocalInt(oPC, "Background_Stage", 6); + DelayCommand(0.1f, StartDynamicConversation("bg_age_cv", oPC, FALSE, FALSE, TRUE, OBJECT_SELF)); + } + else + { // Handle PC responses int nChoice = GetChoice(oPC); @@ -406,8 +413,10 @@ void main() { { if (nChoice == 21) { - // Done - finalize and proceed to next step - ExecuteScript("prof_give_nomore", oPC); + // Done - finalize and proceed to next step + SetPersistantLocalInt(oPC, "CC5_DONE", 1); + SetLocalInt(oItem, "CC5_DONE", 1); + AllowExit(DYNCONV_EXIT_FORCE_EXIT, TRUE, oPC); } else if (nChoice >= 1 && nChoice <= 20) { @@ -618,14 +627,15 @@ void main() { { // Auto-finish SetLocalInt(oItem,"BG_Select",6); - ActionStartConversation(oPC,"bg_final",TRUE); + AllowExit(DYNCONV_EXIT_FORCE_EXIT, TRUE, oPC); + //DelayCommand(0.1f, StartDynamicConversation("bg_age_cv", oPC)); } else { // Return to list, force refresh MarkStageNotSetUp(STAGE_CONFIRM, oPC); MarkStageNotSetUp(STAGE_LIST, oPC); - nStage = STAGE_LIST; // Add this line + nStage = STAGE_LIST; } } else if (nChoice == CHOICE_CONFIRM_NO) diff --git a/src/nss/bg_soclass_cv.nss b/src/nss/bg_soclass_cv.nss index 25fb983..6b3106b 100644 --- a/src/nss/bg_soclass_cv.nss +++ b/src/nss/bg_soclass_cv.nss @@ -1,7 +1,7 @@ // bg_soclass_cv.nss -#include "inc_dynconv" +#include "bg_inc_dynconv" #include "x2_inc_switches" -#include "inc_persist_loca" +#include "bg_inc_p_locals" #include "te_afflic_func" const int STAGE_LIST = 0; @@ -48,7 +48,7 @@ object EnsurePlayerDataObject(object oPC) void main() { object oPC = GetPCSpeaker(); - SendMessageToPC(oPC, "DEBUG: bg_soclass_cv main() entered"); + //SendMessageToPC(oPC, "DEBUG: bg_soclass_cv main() entered"); int nValue = GetLocalInt(oPC, DYNCONV_VARIABLE); int nStage = GetStage(oPC); @@ -132,8 +132,9 @@ void main() } } AllowExit(DYNCONV_EXIT_FORCE_EXIT, TRUE, oPC); - SetPersistantLocalInt(oPC, "CC1_DONE", 1); - DelayCommand(0.1f, StartDynamicConversation("bg_background_cv", oPC)); + SetPersistantLocalInt(oPC, "CC1_DONE", 1); + SetPersistantLocalInt(oPC, "Background_Stage", 2); + DelayCommand(0.1f, StartDynamicConversation("bg_background_cv", oPC, FALSE, FALSE, TRUE, OBJECT_SELF)); } else { // No diff --git a/src/nss/bg_subrace_cv.nss b/src/nss/bg_subrace_cv.nss index b463d29..1da0a4d 100644 --- a/src/nss/bg_subrace_cv.nss +++ b/src/nss/bg_subrace_cv.nss @@ -1,10 +1,9 @@ // bg_subrace_cv.nss -#include "inc_dynconv" +#include "bg_inc_dynconv" #include "x2_inc_switches" -#include "inc_persist_loca" +#include "bg_inc_p_locals" #include "te_afflic_func" - const int STAGE_LIST = 0; const int STAGE_CONFIRM = 1; @@ -28,7 +27,7 @@ object EnsurePlayerDataObject(object oPC) void main() { object oPC = GetPCSpeaker(); - SendMessageToPC(oPC, "DEBUG: bg_subrace_cv main() entered"); + //SendMessageToPC(oPC, "DEBUG: bg_subrace_cv main() entered"); int nValue = GetLocalInt(oPC, DYNCONV_VARIABLE); int nStage = GetStage(oPC); @@ -505,8 +504,9 @@ void main() } } AllowExit(DYNCONV_EXIT_FORCE_EXIT, TRUE, oPC); - SetPersistantLocalInt(oPC, "CC0_DONE", 1); - DelayCommand(0.1f, StartDynamicConversation("bg_soclass_cv", oPC)); + SetPersistantLocalInt(oPC, "CC0_DONE", 1); + SetPersistantLocalInt(oPC, "Background_Stage", 1); + DelayCommand(0.1f, StartDynamicConversation("bg_soclass_cv", oPC, FALSE, FALSE, TRUE, OBJECT_SELF)); } else { // No diff --git a/src/nss/clear_bg_vars.nss b/src/nss/clear_bg_vars.nss index 5f09299..bb330fd 100644 --- a/src/nss/clear_bg_vars.nss +++ b/src/nss/clear_bg_vars.nss @@ -1,11 +1,24 @@ // clear_bg_vars.nss -#include "inc_persist_loca" +#include "bg_inc_p_locals" + +// Ensure the PC Data Object exists; create if missing +object EnsurePlayerDataObject(object oPC) +{ + object oItem = GetItemPossessedBy(oPC, "PC_Data_Object"); + if (!GetIsObjectValid(oItem)) + { + oItem = CreateItemOnObject("pc_data_object", oPC); + SendMessageToPC(oPC, "Language data object recreated"); + WriteTimestampedLogEntry("Language data object recreated"); + } + return oItem; +} void main() { object oPC = OBJECT_SELF; - object oDataObject = GetItemPossessedBy(oPC, "PC_Data_Object"); + object oDataObject = EnsurePlayerDataObject(oPC); if (!GetIsObjectValid(oDataObject)) { SendMessageToPC(oPC, "No PC_Data_Object found on this character."); @@ -61,7 +74,7 @@ void main() DeleteLocalString(oPC, "TEMP_PROF_VAR"); DeleteLocalString(oPC, "TEMP_PROF_GRANT"); DeleteLocalString(oPC, "TEMP_PROF_DESC"); - DeleteLocalString(oPC, "ARR_PROF"); + DeleteLocalString(oPC, "ARR_PROF"); DeletePersistantLocalString(oPC, "ARR_PROF"); DeleteLocalInt(oPC, "PROFICIENCY_COUNT"); DeletePersistantLocalInt(oPC, "PROFICIENCY_COUNT"); @@ -74,6 +87,14 @@ void main() DeleteLocalInt(oDataObject, sSlot); DeletePersistantLocalInt(oPC, sSlot); } - + + // Clear PROFICIENCY_XX variables (new system) + for (i = 0; i < 20; i++) + { + string sSlot = "PROFICIENCY_" + (i < 10 ? "0" : "") + IntToString(i); + DeleteLocalInt(oDataObject, sSlot); + DeletePersistantLocalInt(oPC, sSlot); + } + SendMessageToPC(oPC, "Character creation variables cleared."); } \ No newline at end of file diff --git a/src/nss/cv_background.nss b/src/nss/cv_background.nss new file mode 100644 index 0000000..e013cca --- /dev/null +++ b/src/nss/cv_background.nss @@ -0,0 +1,84 @@ +#include "bg_inc_dynconv" +#include "bg_inc_p_locals" + +// Ensure the PC Data Object exists; create if missing +object EnsurePlayerDataObject(object oPC) +{ + object oItem = GetItemPossessedBy(oPC, "PC_Data_Object"); + if (!GetIsObjectValid(oItem)) + { + oItem = CreateItemOnObject("pc_data_object", oPC); + SendMessageToPC(oPC, "Language data object recreated"); + WriteTimestampedLogEntry("Language data object recreated"); + } + return oItem; +} + +void main() +{ + object oPC = GetLastUsedBy(); + object oItem = EnsurePlayerDataObject(oPC); + + int iState = GetPersistantLocalInt(oPC, "Background_Stage"); + + //int iState = GetLocalInt(oItem, "Background_Stage"); + + if(iState == 0) + { + SendMessageToPC(oPC,"Resuming from Subrace Selection."); + DelayCommand(0.2f, StartDynamicConversation("bg_subrace_cv", oPC, FALSE, FALSE, TRUE, OBJECT_SELF)); + //ActionStartConversation(oPC,"bg_class",TRUE); + } + else if(iState == 1) + { + SendMessageToPC(oPC,"Resuming from Social Class Selection."); + DelayCommand(0.2f, StartDynamicConversation("bg_soclass_cv", oPC, FALSE, FALSE, TRUE, OBJECT_SELF)); + //ActionStartConversation(oPC,"bg_class",TRUE); + } + else if(iState == 2) + { + SendMessageToPC(oPC,"Resuming from Background Selection."); + DelayCommand(0.2f, StartDynamicConversation("bg_background_cv", oPC, FALSE, FALSE, TRUE, OBJECT_SELF)); + //ActionStartConversation(oPC,"bg_background",TRUE); + } + else if(iState == 3) + { + SendMessageToPC(oPC,"Resuming from Deity Selection."); + DelayCommand(0.2f, StartDynamicConversation("bg_deity_cv", oPC, FALSE, FALSE, TRUE, OBJECT_SELF)); + //ActionStartConversation(oPC,"bg_deity",TRUE); + } + else if(iState == 4) + { + SendMessageToPC(oPC,"Resuming from Language Selection."); + DelayCommand(0.2f, StartDynamicConversation("bg_language_cv", oPC, FALSE, FALSE, TRUE, OBJECT_SELF)); + //ActionStartConversation(oPC,"bg_language",TRUE); + } + else if(iState == 5) + { + SendMessageToPC(oPC,"Resuming from Proficiency Selection."); + DelayCommand(0.2f, StartDynamicConversation("bg_profs_cv", oPC, FALSE, FALSE, TRUE, OBJECT_SELF)); + //ActionStartConversation(oPC,"bg_proficiency",TRUE); + } + else if(iState == 6) + { + SendMessageToPC(oPC,"Resuming from Age Modifier Selection."); + DelayCommand(0.2f, StartDynamicConversation("bg_age_cv", oPC, FALSE, FALSE, TRUE, OBJECT_SELF)); + //ActionStartConversation(oPC,"bg_final",TRUE); + } + else if(iState == 7) + { + SendMessageToPC(oPC,"Resuming from Disfigurement Selection."); + DelayCommand(0.2f, StartDynamicConversation("bg_disfig_cv", oPC, FALSE, FALSE, TRUE, OBJECT_SELF)); + //ActionStartConversation(oPC,"bg_disfig",TRUE); + } + else if(iState >= 8) + { + SendMessageToPC(oPC,"You have completed the background selection process."); + } + else + { + SendMessageToPC(oPC,"You have not begun the selection process."); + DelayCommand(0.2f, StartDynamicConversation("bg_subrace_cv", oPC, FALSE, FALSE, TRUE, OBJECT_SELF)); + //ActionStartConversation(oPC,"bg_subrace_cv",TRUE); + } +} diff --git a/src/nss/dmfi_onplychat.nss b/src/nss/dmfi_onplychat.nss new file mode 100644 index 0000000..d51248d --- /dev/null +++ b/src/nss/dmfi_onplychat.nss @@ -0,0 +1,3695 @@ +//:://///////////////////////////////////////////// +//:: DMFI - OnPlayerChat event handler +//:: dmfi_onplychat +//::////////////////////////////////////////////// +/* + Event handler for the module-level OnPlayerChat event. Manages scripter-added + event scripts. +*/ +//::////////////////////////////////////////////// +//:: Created By: Merle, with help from mykael22000 and tsunami282 +//:: Created On: 2007.12.12 +//::////////////////////////////////////////////// +//:: 2007.12.27 tsunami282 - implemented hooking tree + +#include "dmfi_plychat_inc" +#include "mk_inc_editor" +#include "te_lang" +#include "tf_handler" +#include "dmfi_dmwx_inc" +#include "sp_weather" +#include "nwnx_rename" +#include "nwnx_player" +#include "nwnx_chat" +#include "nwnx_creature" +#include "nwnx_admin" +#include "nwnx_webhook_rch" +#include "sfpb_config" +#include "spawn_functions" +#include "ceb_featcheck" +#include "nwnx_inc_const" +#include "te_afflic_func" +//#include "saad_hider" +#include "bg_inc_p_locals" + +const string DMFI_PLAYERCHAT_SCRIPTNAME = "dmfi_plychat_exe"; + +//////////////////////////////////////////////////////////////////////// +void main() +{ + object oItem = OBJECT_INVALID; + object oMod = GetModule(); + object oPC = GetPCChatSpeaker(); + object oArea = GetArea(oPC); + string sArea = GetName(oArea); + string sName = GetName(oPC); + string sChatMessage = GetPCChatMessage(); + int nVolume = GetPCChatVolume(); + +//////////////////////////////////////////////////////////////////////// +// MK Crafting System -- CCOH +//////////////////////////////////////////////////////////////////////// + + int bEditorRunning = GetLocalInt(oPC, g_varEditorRunning); + if (bEditorRunning) // the editor is running + { + int bUseOnPlayerChatEvent = + GetLocalInt(oPC, g_varEditorUseOnPlayerChatEvent); + + if (bUseOnPlayerChatEvent) + { + SetLocalString(oPC, g_varEditorChatMessageString, sChatMessage); + + // the following line is not required but will make everything + // look much better. + SetPCChatMessage(""); // delete the message so it does not + // appear above the player's head + } + return; + } + + +///////////////////////////////////////////////////////////////////// +//Writing System by Bongo, December 2008. OnPlayerChat Script./////// +///////////////////////////////////////////////////////////////////// +//This script stores players chat messages as string when the commands are used. + /* + if(GetStringLeft(sChatMessage, 1)=="#") + { + string sCommand = GetStringLeft(sChatMessage, 3); + if((sCommand=="#T#")||(sCommand=="#W#")||(sCommand=="#S#")||(sCommand=="#C#")) + { + string sIdentity = GetStringLeft(sChatMessage, 3); + DeleteLocalString(oPC, sIdentity); + DeleteLocalString(oPC, "#E#"); + DelayCommand(0.5, SetLocalString(oPC, sIdentity, sChatMessage)); + SendMessageToPC(oPC, sChatMessage); + SetPCChatMessage(""); + return; + } + else if((sCommand=="#E#")||(sCommand=="#R#")||(sCommand=="#F#")) + { + DeleteLocalString(oPC, "#E#"); + DelayCommand(0.5, SetLocalString(oPC, "#E#", sChatMessage)); + SendMessageToPC(oPC, sChatMessage); + SetPCChatMessage(""); + return; + } + } + */ +///////////////////////////////////////////////////////////////////// +//Default DMFI Lines. /////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////// + if (GetIsDM(oPC) || GetIsDMPossessed(oPC)) + { + int maskChannels, bInvoke, bAutoRemove, iHook, bDirtyList = FALSE; + string sChatHandlerScript; + // int bListenAll; + object oRunner; + // SendMessageToPC(GetFirstPC(), "OnPlayerChat - process hooks"); + int nHooks = GetLocalArrayUpperBound(oMod, DMFI_CHATHOOK_HANDLE_ARRAYNAME); + for (iHook = nHooks; iHook > 0; iHook--) // reverse-order execution, last hook gets first dibs + { + // SendMessageToPC(GetFirstPC(), "OnPlayerChat -- process hook #" + IntToString(iHook)); + maskChannels = GetLocalArrayInt(oMod, DMFI_CHATHOOK_CHANNELS_ARRAYNAME, iHook); + // SendMessageToPC(GetFirstPC(), "OnPlayerChat -- channel heard=" + IntToString(nVolume) + ", soughtmask=" + IntToString(maskChannels)); + if (((1 << nVolume) & maskChannels) != 0) // right channel + { + // SendMessageToPC(GetFirstPC(), "OnPlayerChat --- channel matched"); + + bInvoke = FALSE; + if (GetLocalArrayInt(oMod, DMFI_CHATHOOK_LISTENALL_ARRAYNAME, iHook) != FALSE) + { + bInvoke = TRUE; + } + else + { + object oDesiredSpeaker = GetLocalArrayObject(oMod, DMFI_CHATHOOK_SPEAKER_ARRAYNAME, iHook); + if (oPC == oDesiredSpeaker) bInvoke = TRUE; + } + if (bInvoke) // right speaker + { + // SendMessageToPC(GetFirstPC(), "OnPlayerChat --- speaker matched"); + sChatHandlerScript = GetLocalArrayString(oMod, DMFI_CHATHOOK_SCRIPT_ARRAYNAME, iHook); + oRunner = GetLocalArrayObject(oMod, DMFI_CHATHOOK_RUNNER_ARRAYNAME, iHook); + // SendMessageToPC(GetFirstPC(), "OnPlayerChat --- executing script '" + sChatHandlerScript + "' on object '" + GetName(oRunner) +"'"); + ExecuteScript(sChatHandlerScript, oRunner); + bAutoRemove = GetLocalArrayInt(oMod, DMFI_CHATHOOK_AUTOREMOVE_ARRAYNAME, iHook); + if (bAutoRemove) + { + // SendMessageToPC(GetFirstPC(), "OnPlayerChat --- scheduling autoremove"); + bDirtyList = TRUE; + SetLocalArrayInt(oMod, DMFI_CHATHOOK_HANDLE_ARRAYNAME, iHook, 0); + } + } + } + } + + if (bDirtyList) DMFI_ChatHookRemove(0); + + // always execute the DMFI parser + ExecuteScript(DMFI_PLAYERCHAT_SCRIPTNAME, OBJECT_SELF); + } + + + if (nVolume == TALKVOLUME_TALK || nVolume == TALKVOLUME_WHISPER || nVolume == TALKVOLUME_PARTY) + { + string sMessage, sOriginal = GetPCChatMessage(); + location lWhisSource = GetLocation(oPC); + object oWhisp; + + if (GetStringLeft(sOriginal,1) != "-") + { + if (GetIsPC(oPC) && GetStringLength(sOriginal) > 35) + { + SetLocalInt(oPC, "nXPReward", TRUE); + } + + if (GetLocalInt(oPC,"LangOn") == 1) + { + int iLangSpoken = GetLocalInt(oPC,"LangSpoken"); + string LANGCOLOR = ""; + + if (GetLocalInt(oPC,"nDisguiseName") == 1) + { + sName = NWNX_Rename_GetPCNameOverride(oPC); + } + + string sSpeaking = GetLanguageName(iLangSpoken); + + NWNX_WebHook_SendWebHookHTTPS("discordapp.com", WEBHOOK_CHAT_CHANNEL, "Translated ("+sSpeaking+"): "+sOriginal, GetName(oPC)); + + string sTranslate = LANGCOLOR+sName+" ("+sSpeaking+"): "+sOriginal+""; + string sColorOut = GetColorForLanguage(iLangSpoken); + string sOutput=sColorOut+TranslateCommonToLanguage(iLangSpoken,sOriginal)+COLOR_END; + + object oPCs = GetFirstPC(); + while (GetIsObjectValid(oPCs)) + { + if (GetIsDM(oPCs)||GetIsDMPossessed(oPCs)) + { + DelayCommand(0.3,SendMessageToPC(oPCs,sTranslate)); + } + oPCs = GetNextPC(); + } + + object oListener = GetFirstObjectInArea(oArea); + if (nVolume == TALKVOLUME_TALK) + { + while (GetIsObjectValid(oListener)) + { + if (GetIsPC(oListener) && GetObjectHeard(oPC,oListener) && !GetIsDM(oListener)) + { + oItem = GetPlayerDataObject(oListener); + if (GetLocalInt(oItem,IntToString(iLangSpoken)) == 1) + { + DelayCommand(0.3,SendMessageToPC(oListener,sTranslate)); + } + } + oListener = GetNextObjectInArea(oArea); + } + } + else if (nVolume == TALKVOLUME_WHISPER) + { + oWhisp = GetFirstObjectInShape(SHAPE_SPHERE,10.0f,lWhisSource,FALSE,OBJECT_TYPE_CREATURE); + while (GetIsObjectValid(oWhisp)) + { + oItem = GetPlayerDataObject(oWhisp); + float fDist = GetDistanceBetween(oPC, oWhisp); + int nListen = GetSkillRank(SKILL_LISTEN, oWhisp, FALSE); + int bValid = GetIsListenValid(fDist, nListen); + + if (GetObjectHeard(oPC,oWhisp) || fDist <= 3.0f) bValid = 1; + if (bValid != 0 && !GetIsDM(oWhisp) && CheckLanguage(oItem, iLangSpoken)) + { + if (bValid >= 2) + { + NWNX_Chat_SendMessage(NWNX_CHAT_CHANNEL_PLAYER_WHISPER,sOutput,oPC,oWhisp); + } + DelayCommand(0.3,SendMessageToPC(oWhisp,sTranslate)); + } + oWhisp = GetNextObjectInShape(SHAPE_SPHERE,10.0f,lWhisSource,FALSE,OBJECT_TYPE_CREATURE); + } + } + SetPCChatMessage(sOutput); + } + else if (GetStringLeft(sOriginal,1) == "!") + { + object oAnimal = GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION,oPC,1); + if (GetIsObjectValid(oAnimal)) + { + sMessage = GetStringRight(sOriginal,GetStringLength(sOriginal)-1); + AssignCommand(oAnimal,ActionSpeakString(sMessage,nVolume)); + SetPCChatMessage(""); + + struct NWNX_WebHook_Message chatMessage; + chatMessage.sUsername = "On Player Chat Event"; + chatMessage.sTitle = "Animal Companion"; + chatMessage.sColor = "#FFFFFF"; + chatMessage.sAuthorName = GetName(oPC); + chatMessage.sDescription = "Possessing Animal Companion ("+GetName(oAnimal)+")"; + chatMessage.sField1Name = "Playername"; + chatMessage.sField1Value = GetPCPlayerName(oPC); + chatMessage.iField1Inline = TRUE; + chatMessage.sField2Name = "IP"; + chatMessage.sField2Value = GetPCIPAddress(oPC); + chatMessage.iField2Inline = TRUE; + chatMessage.sField3Name = "CDKey"; + chatMessage.sField3Value = GetPCPublicCDKey(oPC); + chatMessage.iField3Inline = TRUE; + chatMessage.sFooterText = "Knights of Noromath Player Event"; + chatMessage.iTimestamp = NWNX_Time_GetTimeStamp(); + string sConstructedMsg = NWNX_WebHook_BuildMessageForWebHook("discordapp.com", WEBHOOK_EVENT_CHANNEL, chatMessage); + NWNX_WebHook_SendWebHookHTTPS("discordapp.com", WEBHOOK_EVENT_CHANNEL, sConstructedMsg); + //NWNX_WebHook_SendWebHookHTTPS("discordapp.com", WEBHOOK_CHAT_CHANNEL, "Possessing Animal Companion ("+GetName(oAnimal)+"): "+sMessage, GetName(oPC)); + } + } + else if (GetStringLeft(sOriginal,1) == "@") + { + object oFamiliar = GetAssociate(ASSOCIATE_TYPE_FAMILIAR,oPC,1); + if (GetIsObjectValid(oFamiliar)) + { + sMessage = GetStringRight(sOriginal,GetStringLength(sOriginal)-1); + AssignCommand(oFamiliar,ActionSpeakString(sMessage,nVolume)); + SetPCChatMessage(""); + + struct NWNX_WebHook_Message chatMessage; + chatMessage.sUsername = "On Player Chat Event"; + chatMessage.sTitle = "Familiar"; + chatMessage.sColor = "#FFFFFF"; + chatMessage.sAuthorName = GetName(oPC); + chatMessage.sDescription = "Possessing Familiar ("+GetName(oFamiliar)+")"; + chatMessage.sField1Name = "Playername"; + chatMessage.sField1Value = GetPCPlayerName(oPC); + chatMessage.iField1Inline = TRUE; + chatMessage.sField2Name = "IP"; + chatMessage.sField2Value = GetPCIPAddress(oPC); + chatMessage.iField2Inline = TRUE; + chatMessage.sField3Name = "CDKey"; + chatMessage.sField3Value = GetPCPublicCDKey(oPC); + chatMessage.iField3Inline = TRUE; + chatMessage.sFooterText = "Knights of Noromath Player Event"; + chatMessage.iTimestamp = NWNX_Time_GetTimeStamp(); + string sConstructedMsg = NWNX_WebHook_BuildMessageForWebHook("discordapp.com", WEBHOOK_EVENT_CHANNEL, chatMessage); + NWNX_WebHook_SendWebHookHTTPS("discordapp.com", WEBHOOK_EVENT_CHANNEL, sConstructedMsg); + //NWNX_WebHook_SendWebHookHTTPS("discordapp.com", WEBHOOK_CHAT_CHANNEL, "Possessing Familiar ("+GetName(oFamiliar)+"): "+sMessage, GetName(oPC)); + } + } + else if (GetStringLeft(sOriginal,1) == "^") + { + object oSummoned = GetAssociate(ASSOCIATE_TYPE_SUMMONED,oPC,1); + if (GetIsObjectValid(oSummoned)) + { + sMessage = GetStringRight(sOriginal,GetStringLength(sOriginal)-1); + AssignCommand(oSummoned,ActionSpeakString(sMessage,nVolume)); + SetPCChatMessage(""); + + struct NWNX_WebHook_Message chatMessage; + chatMessage.sUsername = "On Player Chat Event"; + chatMessage.sTitle = "Summoned Creature"; + chatMessage.sColor = "#FFFFFF"; + chatMessage.sAuthorName = GetName(oPC); + chatMessage.sDescription = "Possessing Summon ("+GetName(oSummoned)+")"; + chatMessage.sField1Name = "Playername"; + chatMessage.sField1Value = GetPCPlayerName(oPC); + chatMessage.iField1Inline = TRUE; + chatMessage.sField2Name = "IP"; + chatMessage.sField2Value = GetPCIPAddress(oPC); + chatMessage.iField2Inline = TRUE; + chatMessage.sField3Name = "CDKey"; + chatMessage.sField3Value = GetPCPublicCDKey(oPC); + chatMessage.iField3Inline = TRUE; + chatMessage.sFooterText = "Knights of Noromath Player Event"; + chatMessage.iTimestamp = NWNX_Time_GetTimeStamp(); + string sConstructedMsg = NWNX_WebHook_BuildMessageForWebHook("discordapp.com", WEBHOOK_EVENT_CHANNEL, chatMessage); + NWNX_WebHook_SendWebHookHTTPS("discordapp.com", WEBHOOK_EVENT_CHANNEL, sConstructedMsg); + //NWNX_WebHook_SendWebHookHTTPS("discordapp.com", WEBHOOK_CHAT_CHANNEL, "Possessing Summon ("+GetName(oSummoned)+"): "+sMessage, GetName(oPC)); + } + } + else if (GetStringLeft(sOriginal,1) == "%") + { + object oDominate = GetAssociate(ASSOCIATE_TYPE_DOMINATED,oPC,1); + if (GetIsObjectValid(oDominate)) + { + sMessage = GetStringRight(sOriginal,GetStringLength(sOriginal)-1); + AssignCommand(oDominate,ActionSpeakString(sMessage,nVolume)); + SetPCChatMessage(""); + + struct NWNX_WebHook_Message chatMessage; + chatMessage.sUsername = "On Player Chat Event"; + chatMessage.sTitle = "Dominated Creature"; + chatMessage.sColor = "#FFFFFF"; + chatMessage.sAuthorName = GetName(oPC); + chatMessage.sDescription = "Possessing Dominated ("+GetName(oDominate)+")"; + chatMessage.sField1Name = "Playername"; + chatMessage.sField1Value = GetPCPlayerName(oPC); + chatMessage.iField1Inline = TRUE; + chatMessage.sField2Name = "IP"; + chatMessage.sField2Value = GetPCIPAddress(oPC); + chatMessage.iField2Inline = TRUE; + chatMessage.sField3Name = "CDKey"; + chatMessage.sField3Value = GetPCPublicCDKey(oPC); + chatMessage.iField3Inline = TRUE; + chatMessage.sFooterText = "Knights of Noromath Player Event"; + chatMessage.iTimestamp = NWNX_Time_GetTimeStamp(); + string sConstructedMsg = NWNX_WebHook_BuildMessageForWebHook("discordapp.com", WEBHOOK_EVENT_CHANNEL, chatMessage); + NWNX_WebHook_SendWebHookHTTPS("discordapp.com", WEBHOOK_EVENT_CHANNEL, sConstructedMsg); + //NWNX_WebHook_SendWebHookHTTPS("discordapp.com", WEBHOOK_CHAT_CHANNEL, "Possessing Dominated ("+GetName(oDominate)+"): "+sMessage, GetName(oPC)); + } + } + else if (GetStringLeft(sOriginal,1) == "$") + { + object oHenchman = GetAssociate(ASSOCIATE_TYPE_HENCHMAN,oPC,1); + if (GetIsObjectValid(oHenchman)) + { + sMessage = GetStringRight(sOriginal,GetStringLength(sOriginal)-1); + AssignCommand(oHenchman,ActionSpeakString(sMessage,nVolume)); + SetPCChatMessage(""); + + struct NWNX_WebHook_Message chatMessage; + chatMessage.sUsername = "On Player Chat Event"; + chatMessage.sTitle = "Henchman"; + chatMessage.sColor = "#FFFFFF"; + chatMessage.sAuthorName = GetName(oPC); + chatMessage.sDescription = "Possessing Henchman ("+GetName(oHenchman)+")"; + chatMessage.sField1Name = "Playername"; + chatMessage.sField1Value = GetPCPlayerName(oPC); + chatMessage.iField1Inline = TRUE; + chatMessage.sField2Name = "IP"; + chatMessage.sField2Value = GetPCIPAddress(oPC); + chatMessage.iField2Inline = TRUE; + chatMessage.sField3Name = "CDKey"; + chatMessage.sField3Value = GetPCPublicCDKey(oPC); + chatMessage.iField3Inline = TRUE; + chatMessage.sFooterText = "Knights of Noromath Player Event"; + chatMessage.iTimestamp = NWNX_Time_GetTimeStamp(); + string sConstructedMsg = NWNX_WebHook_BuildMessageForWebHook("discordapp.com", WEBHOOK_EVENT_CHANNEL, chatMessage); + NWNX_WebHook_SendWebHookHTTPS("discordapp.com", WEBHOOK_EVENT_CHANNEL, sConstructedMsg); + //NWNX_WebHook_SendWebHookHTTPS("discordapp.com", WEBHOOK_CHAT_CHANNEL, "Possessing Henchman ("+GetName(oHenchman)+"): "+sMessage, GetName(oPC)); + } + } + else if (nVolume == TALKVOLUME_WHISPER) + { + oWhisp = GetFirstObjectInShape(SHAPE_SPHERE,10.0f,lWhisSource,FALSE,OBJECT_TYPE_CREATURE); + while (GetIsObjectValid(oWhisp)) + { + float fDist = GetDistanceBetween(oPC, oWhisp); + int nListen = GetSkillRank(SKILL_LISTEN, oWhisp, FALSE); + if (GetIsListenValid(fDist, nListen) >= 2) + { + NWNX_Chat_SendMessage(NWNX_CHAT_CHANNEL_PLAYER_WHISPER,sOriginal,oPC,oWhisp); + } + oWhisp = GetNextObjectInShape(SHAPE_SPHERE,10.0f,lWhisSource,FALSE,OBJECT_TYPE_CREATURE); + } + } + // I am going to attempt my performance system checks in here, and then continue past this with the rest of the script, so don't use returns at the end: Saadow -7/28/24 + else if(GetLocalInt(GetPlayerDataObject(oPC),"PcIsPerforming")>=1) + { + int SaadPerformCheck = GetSkillRank(SKILL_PERFORM,oPC,FALSE)+d20(1); + if (SaadPerformCheck == (GetSkillRank(SKILL_PERFORM,oPC,FALSE)+1)) + { + SetPCChatMessage("(CRITICAL FAILURE)"+""+sOriginal+""); + SetLocalInt(GetPlayerDataObject(oPC),"PcIsPerforming",0); + SetLocalInt(GetPlayerDataObject(oPC),"SaadPerformanceAccumulator",0); + SetLocalInt(oPC,"walk", 0); + NWNX_Player_SetAlwaysWalk(oPC,FALSE); + //RemoveEffect(oPC,saadMusicVis); + effect eLoop = GetFirstEffect(oPC); + while(GetIsEffectValid(eLoop)) + { + if(GetEffectSpellId(eLoop) == 544003) + { + RemoveEffect(oPC, eLoop); + eLoop = GetNextEffect(oPC); + //return; + //eLoop = GetFirstEffect(oPC); + } + else + { + eLoop = GetNextEffect(oPC); + } + } + } + else + { + //Rodor wuz heer + SetPCChatMessage("("+IntToString(SaadPerformCheck)+")"+""+sOriginal+""); + SetLocalInt(GetPlayerDataObject(oPC),"PcIsPerforming",1); + SetLocalInt(GetPlayerDataObject(oPC),"SaadPerformanceAccumulator",GetLocalInt(GetPlayerDataObject(oPC),"SaadPerformanceAccumulator")+1); + } + } + // this should continue as normal past here + } + else + { + sChatMessage = GetStringRight(sChatMessage,GetStringLength(sOriginal)-1); + string sDisguiseName = GetStringRight(sChatMessage,GetStringLength(sOriginal)-6); + SetPCChatMessage(""); + object oDataObject = GetPlayerDataObject(oPC); + + string sCurrCommandArg = parseArgs(sChatMessage,0); + sCurrCommandArg = GetStringLowerCase(sCurrCommandArg); + string sCommandArg2 = parseArgs(sChatMessage,1); + sCommandArg2 = GetStringLowerCase(sCommandArg2); + string sCommandArg3 = parseArgs(sChatMessage,2); + sCommandArg3 = GetStringLowerCase(sCommandArg3); + + int nD20 = d20(1); + string sCrit = ""; + string sRollString = CYAN; + if (nD20 == 1) {sCrit = DARKRED+"\nCritical Failure!"+COLOR_END;} + + if (sCurrCommandArg == "help") + { + string sHelpCommand ="SP Console Command List: \n"; + sHelpCommand += "-help : Gives Command List \n"; + sHelpCommand += "! : Allows you to speak as a valid animal companion. Syntax: !\n"; + sHelpCommand += "@ : Allows you to speak as a valid familiar. Syntax: @\n"; + sHelpCommand += "^ : Allows you to speak as a valid summoned creature. (Use Henchman command if not working) Syntax: ^\n"; + sHelpCommand += "% : Allows you to speak as a valid dominated creature. Syntax: %\n"; + sHelpCommand += "$ : Allows you to speak as a valid henchman. (Use this command if summoned creatures not working) Syntax: $\n"; + sHelpCommand += "-afk : Toggles your status indicated by a flashing glow or whether you're back.\n"; + sHelpCommand += "-animation : Allows you to change your animation style for combat. Does not work while mounted. Certain styles are gated. To see styles available, use \"-animation list\".\n"; + sHelpCommand += "-app : Allows you to save your current appearance (if not polymorphed) or fix it. Syntax: -app \"Save\", -app \"Fix\", or -app \"Set\"(DMs only)\n"; + sHelpCommand += "-debuff : Allows you to completely remove all spells you have cast on yourself. Syntax: -debuff \n"; + sHelpCommand += "-decloak : Allows you to remove only the invisibility effect you have cast on yourself. Syntax: -decloak \n"; + sHelpCommand += "-delete : Allows you to delete a character from your vault. Syntax: -delete \n"; + sHelpCommand += "-disguise : Allows you to disguise yourself using the various toggles on the examine system. (Class Standing, Strength, Dexterity, and Constitution) Syntax: -disguise \"Command\" Use -disguise help for full list.\n"; + sHelpCommand += "-door : Allows you to manage and interact with a nearby settlement door/rentable room. To use properly, stand next the door you wish to modify before typing this command. Syntax: -door \n"; + sHelpCommand += "-emote : Allows you to emote doing a particular action. For a full list of available emotes, use \"-emote list\". Syntax: -emote \"Action\" Ex: -emote sit \n"; + sHelpCommand += "-enchant : Allows enchanters to check the number of enchantments on an item. You must have the item's required feat. \n"; + sHelpCommand += "-helmet : Toggles helmet visibility \n"; + sHelpCommand += "-cloak : Toggles cloak visibility \n"; + sHelpCommand += "-findus : Allows players to locate other players, use findus 1 to allow locating, and findus 0 to disable locating. \n"; + if (GetLevelByClass(CLASS_TYPE_WARLOCK,oPC)>=1) + { + sHelpCommand += "-essence : Allows you to change your Eldritch Blast's essence. Syntax: -essence \"essence\" Ex: -ess frightful \n"; + } + sHelpCommand += "-joust : Allows you to use the Jousting animation instead of standard riding animation. Syntax: -joust \"on/off\" \n"; + if (GetLevelByClass(CLASS_TYPE_MONK,oPC)>=1) + { + sHelpCommand += "-ki : Returns your current ki level.\n"; + } + sHelpCommand += "-lang / -speak : Sets new language to speak. Normal speech going forward will be in that language. To no longer speak a language, use \"-lang common\" Syntax: -lang \"language\" Ex: -lang Alzhedo \n"; + sHelpCommand += "-lockinplace : Makes your character locked in place (uncommandable) until \"-unlock\" command is used.\n"; + sHelpCommand += "-mythic : Allows you to view your current mythic xp stats \n"; + sHelpCommand += "-name : If disguise is on, this will allow you to change your displayed name. This will not persist across reset or log-out/crash. Syntax: - name \"Disguise Name\" Ex: -name Malcolm Reed \n"; + if (GetHasFeat(1203,oPC) || GetLevelByClass(CLASS_TYPE_DRUID,oPC) >= 13 || GetHasFeat(1458,oPC)) + { + sHelpCommand += "-pheno : Allows you to toggle your phenotype on the fly, switching between large and normal. Syntax: -pheno. \n"; + } + sHelpCommand += "-place : Allows you to update the name or description of a placeable. Functions similarly to the writing system. Type \"-place help\" for all available commands. Syntax: -place. \n"; + sHelpCommand += "-pic : Change your portrait. Use \"-pic set\" for something specific or custom. Type \"-pic help\" for more info.\n"; + sHelpCommand += "-piety : Returns your current divine standing in the form of piety (0-100)\n"; + sHelpCommand += "-proficiency : Allows you to select a proficiency if the conversation does not appear for you the first time.\n"; + sHelpCommand += "-rest : Gives next rest time. \n"; + sHelpCommand += "-rename : Allows you to change the name and description of an object in your inventory. Type \"-rename help\" for all available commands. Syntax: -rename.\n"; + sHelpCommand += "-roll : Privately rolls a desired skill or ability. For full list of available rolls, use \"-roll list\" Syntax: -roll \"Ability/Skill/Save\" Ex: -roll will \n"; + sHelpCommand += "-rollp : Experimental public roll function, use \"-rollp list\" Syntax: -rollp \"Ability/Skill/Save\" Ex: -rollp will \n"; + sHelpCommand += "-scale : Adjust your size using a range of -3.0 to 3.0 with 1.0 being Normal sized. Example \"-scale 0.5\" to be 50% of normal size. Value of 0 or \"reset\" return 1.0\n"; + sHelpCommand += "-spellfire : Displays the number of charges in your spellfire mana pool. Syntax: -spellfire\n"; + if (GetLevelByClass(CLASS_TYPE_MONK,oPC)>1) + { + sHelpCommand += "-style : Allows you to cycle through the available styles for your monk order, use \"-style list\" to display all available styles. Use \"-style \" to activate a particular style. \n"; + sHelpCommand += "Use \"-style off\" to turn off your monk style and stop draining ki.\n"; + } + if (GetLevelByClass(CLASS_TYPE_BARD,oPC)>1) + { + sHelpCommand += "-performing : A bard may begin a performance, which will cause the bard to begin walking only. As the bard plays, they may type posts which will automatically roll a performance check. \n"; + sHelpCommand += "As long as they don't critically fail, they can continue performing until they type the command again. Once they type the command a second time, non-hostile NPC's in a colossal area \n"; + sHelpCommand += "will make a will save based on your bard levels, every failed save will result in some small amount of gold earned and experience. \n"; + } + sHelpCommand += "-subdual : Tells you whether Subdual mode is on or off. Type \"-subdual on\" to turn subdual mode on. Type \"-subdual off\" to turn subdual mode off.\n"; + sHelpCommand += "-stuck : Cancels any actions and attempts to 'fix' your character being .. well .. stuck.\n"; + sHelpCommand += "-tournament : Tells you whether Tournament mode is on or off. Type \"-tournament on\" to turn Tournament mode on. Type \"-tournament off\" to turn Tournament mode off.\n"; + sHelpCommand += "-time : Tells you the precise time IG when Timepiece item is in inventory. \n"; + if (GetHasFeat(1439,oPC)) + { + sHelpCommand += "-track : Tracks creatures down within a given area and populates their location if you successfully find their tracks. Type \"-track\" to use this proficiency. Ex: -track \n"; + } + sHelpCommand += "-unlock : Unlocks your character (commandable again). Used after \"-lockinplace\" was used.\n"; + sHelpCommand += "-walk : Sets your character to ALWAYS walk when you travel. Useful for elves. Syntax: -walk. \n"; + sHelpCommand += "-weather : Gives the standard weather feedback message that displays on entry. Syntax: -weather.\n"; + sHelpCommand += "-write : Access to the writing menu for editing pieces of paper in game. Type \"-write help\" for all available commands. Syntax: -write.\n"; + sHelpCommand += "-voice : Assign yourself a voiceset by ID. Example: \"-voice 433\" which is Female, Adventurer. Type \"-voice help #\" for ID list where # is page number starting with 1.\n"; + sHelpCommand += "-xpgp : Toggle the gaining of XP or GP (updates per round). Use 0(off) or 1(on) values. Example: \"-xpgp 1\" will gain GP instead of XP.\n"; + if (GetHasFeat(1599,oPC)) + { + sHelpCommand +="-weave : Gives the general state of the weave in the area.\n"; + } + if (GetIsDM(oPC) || GetIsDMPossessed(oPC)) + { + sHelpCommand +="-setdeadmagic : sets dead magic to the given number (Higher dead magic increases the chance of spell failure).\n"; + sHelpCommand +="-setwildmagic : sets wild magic to the given number (Higher wild magic increases the chance of RANDOMNESS).\n"; + sHelpCommand +="-shopset : sets the price for the given shop unique string., ARG2 = UNIQUE STRING, ARG3 = PRICE\n"; + sHelpCommand +="-ness : resets NESS spawned creatures.\n"; + sHelpCommand +="-copyarea : may copy an area, maybe -Saadow. (still not working)\n"; + } + sHelpCommand +="-bug : Gives a bug report to the DMs"; + SendMessageToPC(oPC,sHelpCommand); + } + else if (sCurrCommandArg == "shout") + { + string sNewOriginal = GetStringRight(sChatMessage,GetStringLength(sOriginal)-7); + //int iLangShout = GetLocalInt(oPC,"LangSpoken"); + int iLangShout = GetPersistantLocalInt(oPC, "LangSpoken"); + object oShoutArea = GetArea(oPC); + string sShoutName = GetName(oPC); + + if (GetLocalInt(oPC,"nDisguiseName") == 1) + { + sShoutName = NWNX_Rename_GetPCNameOverride(oPC); + } + + string sShouting = GetLanguageName(iLangShout); + + if (GetLocalInt(oPC,"LangOn") == 1) + { + NWNX_WebHook_SendWebHookHTTPS("discordapp.com", WEBHOOK_CHAT_CHANNEL, "Translated ("+sShouting+"): "+sNewOriginal, GetName(oPC)); + } + + string sTranslateShout = ""+sShoutName+" ("+sShouting+"): "+sNewOriginal+""; + string sColorShout = GetColorForLanguage(iLangShout); + string sShoutOutput=sColorShout+TranslateCommonToLanguage(iLangShout,sNewOriginal)+COLOR_END; + + object oShout = GetFirstPC(); + object oShoutData; + + while (GetIsObjectValid(oShout)) + { + if (GetIsDM(oShout) || GetIsDMPossessed(oShout)) + { + DelayCommand(0.3,SendMessageToPC(oShout,sTranslateShout)); + } + + if (GetArea(oShout) == oShoutArea) + { + oShoutData = GetItemPossessedBy(oShout,"PC_Data_Object"); + NWNX_Chat_SendMessage(NWNX_CHAT_CHANNEL_PLAYER_TALK,sShoutOutput,oPC,oShout); + if (GetLocalInt(oShoutData,IntToString(iLangShout)) == 1) + { + DelayCommand(0.3,SendMessageToPC(oShout,sTranslateShout)); + } + } + } + } + else if (sCurrCommandArg == "essence" || sCurrCommandArg =="ess" || sCurrCommandArg =="warlock" || sCurrCommandArg == "war") + { + if (GetLevelByClass(CLASS_TYPE_WARLOCK,oPC) >= 1) + { + int nEssence = GetLocalInt(oPC,"nEssence"); + + if ((sCommandArg2 == "frightful" || sCommandArg2 == "fright") && GetHasFeat(1575,oPC)) + { + if (GetLocalInt(oPC,"nEssence") == 1055) {SetLocalInt(oPC,"nEssence",0); SendMessageToPC(oPC,"You are no longer using an essence.");} + else {SetLocalInt(oPC,"nEssence",1055); SendMessageToPC(oPC,"You are now using Frightful Blast essence.");} + } + else if ((sCommandArg2 == "sickening" || sCommandArg2 == "sick") && GetHasFeat(1576,oPC)) + { + if (GetLocalInt(oPC,"nEssence") == 1056) {SetLocalInt(oPC,"nEssence",0); SendMessageToPC(oPC,"You are no longer using an essence.");} + else {SetLocalInt(oPC,"nEssence",1056); SendMessageToPC(oPC,"You are now using Sickening Blast essence.");} + } + else if ((sCommandArg2 == "brimstone" || sCommandArg2 == "brim"|| sCommandArg2 == "fire") && GetHasFeat(1577,oPC)) + { + if (GetLocalInt(oPC,"nEssence") == 1057) {SetLocalInt(oPC,"nEssence",0); SendMessageToPC(oPC,"You are no longer using an essence.");} + else {SetLocalInt(oPC,"nEssence",1057); SendMessageToPC(oPC,"You are now using Brimstone Blast essence.");} + } + else if ((sCommandArg2 == "hellrime" || sCommandArg2 == "hell"|| sCommandArg2 == "cold") && GetHasFeat(1578,oPC)) + { + if (GetLocalInt(oPC,"nEssence") == 1058) {SetLocalInt(oPC,"nEssence",0); SendMessageToPC(oPC,"You are no longer using an essence.");} + else {SetLocalInt(oPC,"nEssence",1058); SendMessageToPC(oPC,"You are now using Hellrime Blast essence.");} + } + else if ((sCommandArg2 == "vitriolic" || sCommandArg2 == "vitr"|| sCommandArg2 == "acid") && GetHasFeat(1579,oPC)) + { + if (GetLocalInt(oPC,"nEssence") == 1059) {SetLocalInt(oPC,"nEssence",0); SendMessageToPC(oPC,"You are no longer using an essence.");} + else {SetLocalInt(oPC,"nEssence",1059); SendMessageToPC(oPC,"You are now using Vitriolic Blast essence.");} + } + else if ((sCommandArg2 == "bewitching" || sCommandArg2 == "bewitch") && GetHasFeat(1580,oPC)) + { + if (GetLocalInt(oPC,"nEssence") == 1060) {SetLocalInt(oPC,"nEssence",0); SendMessageToPC(oPC,"You are no longer using an essence.");} + else {SetLocalInt(oPC,"nEssence",1060); SendMessageToPC(oPC,"You are now using Bewitching Blast essence.");} + } + else if ((sCommandArg2 == "utterdark" || sCommandArg2 == "utter"|| sCommandArg2 == "neg") && GetHasFeat(1581,oPC)) + { + if (GetLocalInt(oPC,"nEssence") == 1061) {SetLocalInt(oPC,"nEssence",0); SendMessageToPC(oPC,"You are no longer using an essence.");} + else {SetLocalInt(oPC,"nEssence",1061); SendMessageToPC(oPC,"You are now using Utterdark Blast essence.");} + } + else if ((sCommandArg2 == "repelling" || sCommandArg2 == "repel") && GetHasFeat(1582,oPC)) + { + if (GetLocalInt(oPC,"nEssence") == 1062) {SetLocalInt(oPC,"nEssence",0); SendMessageToPC(oPC,"You are no longer using an essence.");} + else {SetLocalInt(oPC,"nEssence",1062); SendMessageToPC(oPC,"You are now using Repelling Blast essence.");} + } + else if (sCommandArg2 == "none" || sCommandArg2 == "off") + { + SendMessageToPC(oPC,"Essence type set to none."); + SetLocalInt(oPC,"nEssence",0); + } + else + { + SendMessageToPC(oPC,"Essence type not recognized - Essence set to None."); + SetLocalInt(oPC,"nEssence",0); + } + } + else + { + SendMessageToPC(oPC,"Command not available to non-warlocks."); + } + } + else if (sCurrCommandArg == "enchant") + { + object oEnchantItem = GetLocalObject(oPC,"ObjectEdit"); + string sFeat; + switch (GetBaseItemType(oEnchantItem)) + { + case BASE_ITEM_AMULET: + case BASE_ITEM_BELT: + case BASE_ITEM_BOOTS: + case BASE_ITEM_CLOAK: + case BASE_ITEM_GLOVES: + case BASE_ITEM_BRACER: + case BASE_ITEM_HELMET: + sFeat = "Craft_Wonderous_Item"; + break; + case BASE_ITEM_RING: + sFeat = "Forge_Ring"; + break; + case BASE_ITEM_ARMOR: + case BASE_ITEM_BASTARDSWORD: + case BASE_ITEM_BATTLEAXE: + case BASE_ITEM_CLUB: + case BASE_ITEM_DAGGER: + case BASE_ITEM_DART: + case BASE_ITEM_DIREMACE: + case BASE_ITEM_DOUBLEAXE: + case BASE_ITEM_DWARVENWARAXE: + case BASE_ITEM_GREATAXE: + case BASE_ITEM_GREATSWORD: + case BASE_ITEM_HALBERD: + case BASE_ITEM_HEAVYCROSSBOW: + case BASE_ITEM_HEAVYFLAIL: + case BASE_ITEM_KAMA: + case BASE_ITEM_KATANA: + case BASE_ITEM_KUKRI: + case BASE_ITEM_LARGESHIELD: + case BASE_ITEM_LIGHTCROSSBOW: + case BASE_ITEM_LIGHTFLAIL: + case BASE_ITEM_LIGHTHAMMER: + case BASE_ITEM_LIGHTMACE: + case BASE_ITEM_LONGBOW: + case BASE_ITEM_LONGSWORD: + case BASE_ITEM_MORNINGSTAR: + case BASE_ITEM_QUARTERSTAFF: + case BASE_ITEM_RAPIER: + case BASE_ITEM_SCIMITAR: + case BASE_ITEM_SCYTHE: + case BASE_ITEM_SHORTBOW: + case BASE_ITEM_SHORTSPEAR: + case BASE_ITEM_SHORTSWORD: + case BASE_ITEM_SHURIKEN: + case BASE_ITEM_SICKLE: + case BASE_ITEM_SLING: + case BASE_ITEM_SMALLSHIELD: + case BASE_ITEM_THROWINGAXE: + case BASE_ITEM_TOWERSHIELD: + case BASE_ITEM_TRIDENT: + case BASE_ITEM_TWOBLADEDSWORD: + case BASE_ITEM_WARHAMMER: + case BASE_ITEM_WHIP: + sFeat = "Craft_Magic_Arms_And_Armor"; + break; + } + if (FeatCheck(sFeat, oPC)) + { + string sEnchantReturn = ""; + sEnchantReturn += "Enchantments Used: "; + if (sFeat == "Craft_Magic_Arms_And_Armor") + { + sEnchantReturn += IntToString(GetLocalInt(oEnchantItem, "ArmorCap") + GetLocalInt(oEnchantItem, "WeaponCap")); + } + if (sFeat == "Forge_Ring") + { + sEnchantReturn += IntToString(GetLocalInt(oEnchantItem, "AccessoryCap")); + } + if (sFeat == "Craft_Magic_Arms_And_Armor") + { + sEnchantReturn += IntToString(GetLocalInt(oEnchantItem, "AccessoryCap")); + } + SendMessageToPC(oPC, sEnchantReturn); + return; + } + else + { + SendMessageToPC(oPC, "You do not have the feat used to enchant this item, and as such cannot tell its enchantments"); + } + } + else if (sCurrCommandArg == "copyarea") + { + if (GetIsDM(oPC) || GetIsDMPossessed(oPC)) + { + SendMessageToPC(oPC,"This command is not working yet, sorry. -Saadow");//CreateArea(GetArea(oPC),"",0,0);//GetArea(oPC),(ObjectToString(oPC)+GetArea(oPC)),GetName(GetArea(oPC))+"_"+ObjectToString(oPC)); + return; + } + else + { + SendMessageToPC(oPC,"This command is available only to Dungeonmasters or Questmasters."); + return; + } + } + else if (sCurrCommandArg == "rename") + { + object oRenameObject = GetLocalObject(oPC,"ObjectEdit"); + string sObjText; + string sItemDesc; + if (!GetIsObjectValid(oRenameObject) && GetItemPossessor(oRenameObject) != oPC) + { + SendMessageToPC(oPC,"You must select an object with the Combiner (silver star) tool to be able to edit the name or description of an object."); + return; + } + + if (sCommandArg2 == "help") + { + SendMessageToPC(oPC,"The Rename Command allows you to edit the title and description of any valid item in your inventory.\nThe following commands are available:\nhelp - gives this help message\ntitle - changes the title of an item\nadd - adds your text in addition to the text already on the item\nnew - overwrites all text on the item as your text\nline - starts your added text on new line."); + } + else if (sCommandArg2 == "title") + { + sObjText = GetStringRight(sChatMessage,GetStringLength(sOriginal)-14); + SetName(oRenameObject,sObjText); + SendMessageToPC(oPC,"Title of item set as: "+sObjText); + } + else if (sCommandArg2 == "add") + { + sObjText = GetStringRight(sChatMessage,GetStringLength(sOriginal)-12); + sItemDesc = GetDescription(oRenameObject,FALSE,TRUE); + SetDescription(oRenameObject,sItemDesc+" "+sObjText,TRUE); + SendMessageToPC(oPC,"Text added to item as: "+sObjText); + } + else if (sCommandArg2 == "line") + { + sObjText = GetStringRight(sChatMessage,GetStringLength(sOriginal)-13); + sItemDesc = GetDescription(oRenameObject,FALSE,TRUE); + SetDescription(oRenameObject,sItemDesc+"\n"+sObjText,TRUE); + SendMessageToPC(oPC,"Text added to new line of item description as: "+sObjText); + } + else if (sCommandArg2 == "new") + { + sObjText = GetStringRight(sChatMessage,GetStringLength(sOriginal)-12); + sItemDesc = GetDescription(oRenameObject,FALSE,TRUE); + SetDescription(oRenameObject,sObjText,TRUE); + SendMessageToPC(oPC,"New description given to line of item description as: "+sObjText); + } + } + else if (sCurrCommandArg == "proficiency"|| sCurrCommandArg == "prof") + { + if (GetLocalInt(GetItemPossessedBy(oPC,"PC_Data_Object"),"Prof") >= 1) + { + SendMessageToPC(oPC,"Please choose an additional proficiency. Proficiencies cannot be banked, and will become void if you do not choose an additional proficiency."); + AssignCommand(oPC,ActionStartConversation(oPC,"te_prof_lvl",TRUE,FALSE)); + } + else + { + SendMessageToPC(oPC,"You do not have any proficiencies that may be selected."); + } + } + else if (sCurrCommandArg == "mastery") + { + if ( sCommandArg2 == "necromancy" || sCommandArg2 == "nec" ) SendMessageToPC(oPC,IntToString(GetLocalInt(GetItemPossessedBy(oPC,"PC_Data_Object"),"Necromancy"))+" spellmastery in necromancy."); + else if (sCommandArg2 == "transmutation" || sCommandArg2 == "tra" ) SendMessageToPC(oPC,IntToString(GetLocalInt(GetItemPossessedBy(oPC,"PC_Data_Object"),"Transmutation"))+" spellmastery in transmutation."); + else if (sCommandArg2 == "abjuration" || sCommandArg2 == "abj" ) SendMessageToPC(oPC,IntToString(GetLocalInt(GetItemPossessedBy(oPC,"PC_Data_Object"),"Abjuration"))+" spellmastery in abjuration."); + else if (sCommandArg2 == "divination" || sCommandArg2 == "div" ) SendMessageToPC(oPC,IntToString(GetLocalInt(GetItemPossessedBy(oPC,"PC_Data_Object"),"Divination"))+" spellmastery in divination."); + else if (sCommandArg2 == "conjuration" || sCommandArg2 == "con" ) SendMessageToPC(oPC,IntToString(GetLocalInt(GetItemPossessedBy(oPC,"PC_Data_Object"),"Conjuration"))+" spellmastery in conjuration."); + else if (sCommandArg2 == "enchantment" || sCommandArg2 == "enc" ) SendMessageToPC(oPC,IntToString(GetLocalInt(GetItemPossessedBy(oPC,"PC_Data_Object"),"Enchantment"))+" spellmastery in enchantment."); + else if (sCommandArg2 == "evocation" || sCommandArg2 == "evo" ) SendMessageToPC(oPC,IntToString(GetLocalInt(GetItemPossessedBy(oPC,"PC_Data_Object"),"Evocation"))+" spellmastery in evocation."); + else if (sCommandArg2 == "illusion" || sCommandArg2 == "ill" ) SendMessageToPC(oPC,IntToString(GetLocalInt(GetItemPossessedBy(oPC,"PC_Data_Object"),"Illusion"))+" spellmastery in illusion."); + } + else if (sCurrCommandArg == "mythic") + { + object oData = GetPlayerDataObject(oPC); + SendMessageToPC(oPC,"A new point in each skill is achieved at 1000, 2000, 4000, and 8000 Mythic Points."); + SendMessageToPC(oPC,IntToString(GetLocalInt(oData,"MythicSTR"))+" Mythic Strength Points"); + SendMessageToPC(oPC,IntToString(GetLocalInt(oData,"MythicDEX"))+" Mythic Dexterity Points"); + SendMessageToPC(oPC,IntToString(GetLocalInt(oData,"MythicCON"))+" Mythic Constitution Points"); + SendMessageToPC(oPC,IntToString(GetLocalInt(oData,"MythicINT"))+" Mythic Intelligence Points"); + SendMessageToPC(oPC,IntToString(GetLocalInt(oData,"MythicWIS"))+" Mythic Wisdom Points"); + SendMessageToPC(oPC,IntToString(GetLocalInt(oData,"MythicCHA"))+" Mythic Charisma Points"); + } + else if (sCurrCommandArg == "afk") + { + object oData = GetPlayerDataObject(oPC); + if (GetLocalInt(oData,"isAFK")) + { + // Remove AFK status + SetCommandable(TRUE, oPC); + FloatingTextStringOnCreature("Is back from being AFK", oPC); + RemoveEffectsByTag(oPC, "AFK"); + + SetLocalInt(oData, "isAFK", FALSE); + } + else + { + // Apply visual effect, display message, and lock character in place + SetCommandable(FALSE, oPC); + FloatingTextStringOnCreature("Is now AFK", oPC); + effect eAFK = EffectVisualEffect(VFX_DUR_AURA_PULSE_RED_WHITE); + AddSubraceEffect(oPC, eAFK, "AFK"); + + SetLocalInt(oData, "isAFK", TRUE); + } + } + // SAADOW NEW COMMAND IN HERE, CLOAK AND HELMET, HIDE EITHER + else if (sCurrCommandArg == "cloak") + { + // Determine item slot of the item to hide/show + int nSlot = INVENTORY_SLOT_CLOAK; + // Determine the item to hide/show + oItem = GetItemInSlot(nSlot, oPC); + // If there's no item in the slot, then exit + if (!GetIsObjectValid(oItem)) + { + return; + } + + // Determine if currently shown + int nShown = GetHiddenWhenEquipped(oItem); + // Toggle the state + SetHiddenWhenEquipped(oItem, (nShown == TRUE ? FALSE : TRUE)); + // Send feedback + SendMessageToPC(oPC, "Cloak toggled!"); + // Exit + } + else if (sCurrCommandArg == "helmet") + { + // Determine item slot of the item to hide/show + int nSlot = INVENTORY_SLOT_HEAD; + // Determine the item to hide/show + oItem = GetItemInSlot(nSlot, oPC); + // If there's no item in the slot, then exit + if (!GetIsObjectValid(oItem)) + { + return; + } + + // Determine if currently shown + int nShown = GetHiddenWhenEquipped(oItem); + + // Toggle the state + SetHiddenWhenEquipped(oItem, (nShown == TRUE ? FALSE : TRUE)); + + // Send feedback + SendMessageToPC(oPC, "Helmet toggled!"); + + // Exit + + + } + //End new command, 6/9/24 + else if (sCurrCommandArg == "animation" || sCurrCommandArg == "anim" || sCurrCommandArg == "anime") + { + if (sCommandArg2 == "list") + { + string sAnimList = "You are able to use the following combat styles:\n"; + + if (GetLevelByClass(CLASS_TYPE_DRUID,oPC) >= 1 || + GetLevelByClass(CLASS_TYPE_FIGHTER,oPC) >= 1 || + GetLevelByClass(CLASS_TYPE_RANGER,oPC) >= 1 || + GetLevelByClass(CLASS_TYPE_PALADIN,oPC) >= 1 || + GetLevelByClass(CLASS_TYPE_CLERIC,oPC) >= 1) + { + sAnimList += "- Sword Master\n"; + } + if (GetLevelByClass(CLASS_TYPE_DRUID,oPC) >= 1 || + GetLevelByClass(CLASS_TYPE_FIGHTER,oPC) >= 1 || + GetLevelByClass(CLASS_TYPE_RANGER,oPC) >= 1 || + GetLevelByClass(CLASS_TYPE_PALADIN,oPC) >= 1 || + GetLevelByClass(CLASS_TYPE_CLERIC,oPC) >= 1) + { + sAnimList += "- Brute\n"; + } + if (GetLevelByClass(CLASS_TYPE_DRUID,oPC) >= 1 || + GetLevelByClass(CLASS_TYPE_FIGHTER,oPC) >= 1 || + GetLevelByClass(CLASS_TYPE_RANGER,oPC) >= 1 || + GetLevelByClass(CLASS_TYPE_PALADIN,oPC) >= 1) + { + sAnimList += "- Soldier's Stance\n"; + } + if (GetLevelByClass(CLASS_TYPE_ASSASSIN,oPC) >= 1 || + GetLevelByClass(CLASS_TYPE_RANGER,oPC) >= 1 || + GetLevelByClass(CLASS_TYPE_ROGUE,oPC) >= 1 ) + { + sAnimList += "- Twin Jambiyas\n"; + } + if (GetLevelByClass(CLASS_TYPE_FIGHTER,oPC) >= 1 || + GetLevelByClass(CLASS_TYPE_BARD,oPC) >= 1 || + GetLevelByClass(CLASS_TYPE_RANGER,oPC) >= 1 || + GetLevelByClass(CLASS_TYPE_BLACKCOAT,oPC) >= 1 || + GetLevelByClass(CLASS_TYPE_ROGUE,oPC) >= 1 ) + { + sAnimList += "- Duelist\n"; + } + if (GetLevelByClass(CLASS_TYPE_CLERIC,oPC) >= 1 || + GetLevelByClass(CLASS_TYPE_WIZARD,oPC) >= 1 || + GetLevelByClass(CLASS_TYPE_SORCERER,oPC) >= 1 || + GetLevelByClass(CLASS_TYPE_SPELLFIRE,oPC) >= 1 || + GetLevelByClass(CLASS_TYPE_DRUID,oPC) >= 1 || + GetLevelByClass(CLASS_TYPE_WARLOCK,oPC) >= 1 ) + { + sAnimList += "- Arcane\n"; + } + sAnimList += "To activate a fighting style, type \"-animation