diff --git a/Release/PRC8_202400611.7z b/Release/PRC8_202400611.7z new file mode 100644 index 00000000..4a5d77b6 Binary files /dev/null and b/Release/PRC8_202400611.7z differ diff --git a/_backup/PRC8_20240407.7z b/_backup/PRC8_20240407.7z new file mode 100644 index 00000000..aaa5e9dd Binary files /dev/null and b/_backup/PRC8_20240407.7z differ diff --git a/nwn/nwnprc/_removed/ccc_inc_convo.nss b/nwn/nwnprc/_removed/ccc_inc_convo.nss new file mode 100644 index 00000000..3bda6e9f --- /dev/null +++ b/nwn/nwnprc/_removed/ccc_inc_convo.nss @@ -0,0 +1,1845 @@ +#include "ccc_inc_misc" + +// sets up the header and the choices for each stage of the convoCC +void DoHeaderAndChoices(int nStage); + +// processes the player choices for each stage of the convoCC +int HandleChoice(int nStage, int nChoice); + +void DoHeaderAndChoices(int nStage) +{ + string sText; + string sName; + int i = 0; // loop counter + switch(nStage) + { + case STAGE_INTRODUCTION: { + sText = "This is the PRC Conversation Character Creator (CCC).\n"; + sText+= "This is a replicate of the bioware character creator, but it will allow you to select custom content at level 1. "; + sText+= "Simply follow the step by step instructions and select what you want. "; + sText+= "If you dont get all the options you think you should at a stage, select one, then select No at the confirmation step."; + SetHeader(sText); + // setup the choices + AddChoice("continue", 0); + MarkStageSetUp(nStage); + break; + } + case STAGE_GENDER: { + sText = GetStringByStrRef(158); + SetHeader(sText); + // set up the choices + Do2daLoop("gender", "name", GetPRCSwitch(FILE_END_GENDER)); + MarkStageSetUp(nStage); + break; + } + case STAGE_GENDER_CHECK: { + sText = GetStringByStrRef(16824209) + " "; // You have selected: + sText+= GetStringByStrRef(StringToInt(Get2DACache("gender", "NAME", GetLocalInt(OBJECT_SELF, "Gender")))); + sText+= "\n"+GetStringByStrRef(16824210); + SetHeader(sText); + // choices Y/N + AddChoice(GetStringByStrRef(4753), -1); // no + AddChoice(GetStringByStrRef(4752), 1); // yes + MarkStageSetUp(nStage); + break; + } + case STAGE_RACE: { + sText = GetStringByStrRef(162); // Select a Race for your Character + SetHeader(sText); + // set up choices + // try with waiting set up first + SetLocalInt(OBJECT_SELF, "DynConv_Waiting", TRUE); + DelayCommand(0.01, DoRacialtypesLoop()); + MarkStageSetUp(nStage); + SetDefaultTokens(); + break; + } + case STAGE_RACE_CHECK: { + sText = GetStringByStrRef(16824209) + " "; // You have selected: + sText += GetStringByStrRef(StringToInt(Get2DACache("racialtypes", "Name", GetLocalInt(OBJECT_SELF, "Race")))); + sText += "\n"; + sText+= GetStringByStrRef(StringToInt(Get2DACache("racialtypes", "Description", GetLocalInt(OBJECT_SELF, "Race")))); + sText+= "\n"+GetStringByStrRef(16824210); // Is this correct? + SetHeader(sText); + // choices Y/N + AddChoice(GetStringByStrRef(4753), -1); // no + AddChoice(GetStringByStrRef(4752), 1); // yes + MarkStageSetUp(nStage); + break; + } + case STAGE_CLASS: { + sText = GetStringByStrRef(61920); // Select a Class for Your Character + SetHeader(sText); + // set up choices + SetLocalInt(OBJECT_SELF, "DynConv_Waiting", TRUE); + DelayCommand(0.01, DoClassesLoop()); + MarkStageSetUp(nStage); + SetDefaultTokens(); + break; + } + case STAGE_CLASS_CHECK: { + sText = GetStringByStrRef(16824209) + " "; // You have selected: + sText += GetStringByStrRef(StringToInt(Get2DACache("classes", "Name", GetLocalInt(OBJECT_SELF, "Class")))); + sText += "\n"; + sText+= GetStringByStrRef(StringToInt(Get2DACache("classes", "Description", GetLocalInt(OBJECT_SELF, "Class")))); + sText+= "\n"+GetStringByStrRef(16824210); // Is this correct? + SetHeader(sText); + // choices Y/N + AddChoice(GetStringByStrRef(4753), -1); // no + AddChoice(GetStringByStrRef(4752), 1); // yes + MarkStageSetUp(nStage); + break; + } + case STAGE_ALIGNMENT: { + sText = GetStringByStrRef(111); // Select an Alignment for your Character + SetHeader(sText); + // get the restriction info from classes.2da + SetLocalInt(OBJECT_SELF, "DynConv_Waiting", TRUE); + int nClass = GetLocalInt(OBJECT_SELF, "Class"); + int iAlignRestrict = HexToInt(Get2DACache("classes", "AlignRestrict",nClass)); + int iAlignRstrctType = HexToInt(Get2DACache("classes", "AlignRstrctType",nClass)); + int iInvertRestrict = HexToInt(Get2DACache("classes", "InvertRestrict",nClass)); + // set up choices + if(GetIsValidAlignment(ALIGNMENT_LAWFUL, ALIGNMENT_GOOD,iAlignRestrict, iAlignRstrctType, iInvertRestrict)) + AddChoice(GetStringByStrRef(112), 112); + if(GetIsValidAlignment(ALIGNMENT_NEUTRAL, ALIGNMENT_GOOD,iAlignRestrict, iAlignRstrctType, iInvertRestrict)) + AddChoice(GetStringByStrRef(115), 115); + if(GetIsValidAlignment(ALIGNMENT_CHAOTIC, ALIGNMENT_GOOD,iAlignRestrict, iAlignRstrctType, iInvertRestrict)) + AddChoice(GetStringByStrRef(118), 118); + if(GetIsValidAlignment(ALIGNMENT_LAWFUL, ALIGNMENT_NEUTRAL,iAlignRestrict, iAlignRstrctType, iInvertRestrict)) + AddChoice(GetStringByStrRef(113), 113); + if(GetIsValidAlignment(ALIGNMENT_NEUTRAL, ALIGNMENT_NEUTRAL,iAlignRestrict, iAlignRstrctType, iInvertRestrict)) + AddChoice(GetStringByStrRef(116), 116); + if(GetIsValidAlignment(ALIGNMENT_CHAOTIC, ALIGNMENT_NEUTRAL,iAlignRestrict, iAlignRstrctType, iInvertRestrict)) + AddChoice(GetStringByStrRef(119), 119); + if(GetIsValidAlignment(ALIGNMENT_LAWFUL, ALIGNMENT_EVIL,iAlignRestrict, iAlignRstrctType, iInvertRestrict)) + AddChoice(GetStringByStrRef(114), 114); + if(GetIsValidAlignment(ALIGNMENT_NEUTRAL, ALIGNMENT_EVIL,iAlignRestrict, iAlignRstrctType, iInvertRestrict)) + AddChoice(GetStringByStrRef(117), 117); + if(GetIsValidAlignment(ALIGNMENT_CHAOTIC, ALIGNMENT_EVIL,iAlignRestrict, iAlignRstrctType, iInvertRestrict)) + AddChoice(GetStringByStrRef(120), 120); + DelayCommand(0.01, DeleteLocalInt(OBJECT_SELF, "DynConv_Waiting")); + MarkStageSetUp(nStage); + break; + } + case STAGE_ALIGNMENT_CHECK: { + sText = GetStringByStrRef(16824209) + " "; // You have selected: + int nStrRef = GetLocalInt(OBJECT_SELF, "AlignChoice"); // strref for the alignment + sText += GetStringByStrRef(nStrRef); + sText += "\n"+GetStringByStrRef(16824210); // Is this correct? + SetHeader(sText); + // choices Y/N + AddChoice(GetStringByStrRef(4753), -1); // no + AddChoice(GetStringByStrRef(4752), 1); // yes + MarkStageSetUp(nStage); + break; + } + case STAGE_ABILITY: { + // the first time through this stage set everything up + if(GetLocalInt(OBJECT_SELF, "Str") == 0) + { + // get the starting points to allocate + int nPoints = GetPRCSwitch(PRC_CONVOCC_STAT_POINTS); + if(nPoints == 0) + nPoints = 30; // default + SetLocalInt(OBJECT_SELF, "Points", nPoints); + // get the max stat level (before racial modifiers) + int nMaxStat = GetPRCSwitch(PRC_CONVOCC_MAX_STAT); + if(nMaxStat == 0) + nMaxStat = 18; // default + SetLocalInt(OBJECT_SELF, "MaxStat", nMaxStat); + // set the starting stat values + SetLocalInt(OBJECT_SELF, "Str", 8); + SetLocalInt(OBJECT_SELF, "Dex", 8); + SetLocalInt(OBJECT_SELF, "Con", 8); + SetLocalInt(OBJECT_SELF, "Int", 8); + SetLocalInt(OBJECT_SELF, "Wis", 8); + SetLocalInt(OBJECT_SELF, "Cha", 8); + } + sText = GetStringByStrRef(130) + "\n"; // Select Ability Scores for your Character + sText += GetStringByStrRef(138) + ": "; // Remaining Points + sText += IntToString(GetLocalInt(OBJECT_SELF, "Points")); + SetHeader(sText); + // get the racial adjustment + int nRace = GetLocalInt(OBJECT_SELF, "Race"); + string sStrAdjust = Get2DACache("racialtypes", "StrAdjust", nRace); + string sDexAdjust = Get2DACache("racialtypes", "DexAdjust", nRace); + string sConAdjust = Get2DACache("racialtypes", "ConAdjust", nRace); + string sIntAdjust = Get2DACache("racialtypes", "IntAdjust", nRace); + string sWisAdjust = Get2DACache("racialtypes", "WisAdjust", nRace); + string sChaAdjust = Get2DACache("racialtypes", "ChaAdjust", nRace); + // set up the choices in " (racial <+/-modifier>) . Cost to increase " format + AddAbilityChoice(GetLocalInt(OBJECT_SELF, "Str"), GetStringByStrRef(135), sStrAdjust, ABILITY_STRENGTH); + AddAbilityChoice(GetLocalInt(OBJECT_SELF, "Dex"), GetStringByStrRef(133), sDexAdjust, ABILITY_DEXTERITY); + AddAbilityChoice(GetLocalInt(OBJECT_SELF, "Con"), GetStringByStrRef(132), sConAdjust, ABILITY_CONSTITUTION); + AddAbilityChoice(GetLocalInt(OBJECT_SELF, "Int"), GetStringByStrRef(134), sIntAdjust, ABILITY_INTELLIGENCE); + AddAbilityChoice(GetLocalInt(OBJECT_SELF, "Wis"), GetStringByStrRef(136), sWisAdjust, ABILITY_WISDOM); + AddAbilityChoice(GetLocalInt(OBJECT_SELF, "Cha"), GetStringByStrRef(131), sChaAdjust, ABILITY_CHARISMA); + MarkStageSetUp(nStage); + break; + } + case STAGE_ABILITY_CHECK: { + sText = GetStringByStrRef(16824209) + "\n"; // You have selected: + // get the racial adjustment + int nRace = GetLocalInt(OBJECT_SELF, "Race"); + string sStrAdjust = Get2DACache("racialtypes", "StrAdjust", nRace); + string sDexAdjust = Get2DACache("racialtypes", "DexAdjust", nRace); + string sConAdjust = Get2DACache("racialtypes", "ConAdjust", nRace); + string sIntAdjust = Get2DACache("racialtypes", "IntAdjust", nRace); + string sWisAdjust = Get2DACache("racialtypes", "WisAdjust", nRace); + string sChaAdjust = Get2DACache("racialtypes", "ChaAdjust", nRace); + sText += GetStringByStrRef(135) + ": " + IntToString(GetLocalInt(OBJECT_SELF, "Str") + StringToInt(sStrAdjust)) + "\n"; // str + sText += GetStringByStrRef(133) + ": " + IntToString(GetLocalInt(OBJECT_SELF, "Dex") + StringToInt(sDexAdjust)) + "\n"; // dex + sText += GetStringByStrRef(132) + ": " + IntToString(GetLocalInt(OBJECT_SELF, "Con") + StringToInt(sConAdjust)) + "\n"; // con + sText += GetStringByStrRef(134) + ": " + IntToString(GetLocalInt(OBJECT_SELF, "Int") + StringToInt(sIntAdjust)) + "\n"; // int + sText += GetStringByStrRef(136) + ": " + IntToString(GetLocalInt(OBJECT_SELF, "Wis") + StringToInt(sWisAdjust)) + "\n"; // wis + sText += GetStringByStrRef(131) + ": " + IntToString(GetLocalInt(OBJECT_SELF, "Cha") + StringToInt(sChaAdjust)) + "\n"; // cha + sText += "\n" + GetStringByStrRef(16824210); // Is this correct? + SetHeader(sText); + // choices Y/N + AddChoice(GetStringByStrRef(4753), -1); // no + AddChoice(GetStringByStrRef(4752), 1); // yes + MarkStageSetUp(nStage); + break; + } + case STAGE_SKILL: { + // the first time through this stage set everything up + int nPoints = GetLocalInt(OBJECT_SELF, "Points"); + if(nPoints == 0) + { + SetLocalInt(OBJECT_SELF, "DynConv_Waiting", TRUE); + //calculate number of points + nPoints += StringToInt(Get2DACache("classes", "SkillPointBase", GetLocalInt(OBJECT_SELF, "Class"))); + // calculate the intelligence bonus/penalty + int nInt = GetLocalInt(OBJECT_SELF, "Int"); + int nRace = GetLocalInt(OBJECT_SELF, "Race"); + nPoints += (nInt-10+StringToInt(Get2DACache("racialtypes", "IntAdjust", nRace)))/2; + if(GetPRCSwitch(PRC_CONVOCC_SKILL_MULTIPLIER)) + nPoints *= GetPRCSwitch(PRC_CONVOCC_SKILL_MULTIPLIER); + else + nPoints *= 4; + // humans get an extra 4 skill points at level 1 + if (GetLocalInt(OBJECT_SELF, "Race") == RACIAL_TYPE_HUMAN) + nPoints += 4; + nPoints += GetPRCSwitch(PRC_CONVOCC_SKILL_BONUS); + // minimum of 4, regardless of int + if(nPoints < 4) + nPoints = 4; + SetLocalInt(OBJECT_SELF, "Points", nPoints); + DelayCommand(0.01, DoSkillsLoop()); + } + else + DoSkillsLoop(); + // do header + sText = GetStringByStrRef(396) + "\n"; // Allocate skill points + sText += GetStringByStrRef(395) + ": "; // Remaining Points + sText += IntToString(GetLocalInt(OBJECT_SELF, "Points")); + SetHeader(sText); + /* Hack - Returning to the skill selection stage, restore the + * offset to be the same as it was choosing the skill. + */ + if(GetLocalInt(OBJECT_SELF, "SkillListChoiceOffset")) + { + SetLocalInt(OBJECT_SELF, DYNCONV_CHOICEOFFSET, GetLocalInt(OBJECT_SELF, "SkillListChoiceOffset") - 1); + DeleteLocalInt(OBJECT_SELF, "SkillListChoiceOffset"); + } + MarkStageSetUp(nStage); + SetDefaultTokens(); + break; + } + case STAGE_SKILL_CHECK: { + sText = GetStringByStrRef(16824209) + "\n"; // You have selected: + if(GetPRCSwitch(PRC_CONVOCC_ALLOW_SKILL_POINT_ROLLOVER)) + { + sText += "Stored skill points: "; + sText += IntToString(GetLocalInt(OBJECT_SELF, "SavedSkillPoints")) + "\n"; + } + // loop through the "Skills" array + for(i=0; i <= GetPRCSwitch(FILE_END_SKILLS); i++) // the array can't be bigger than the skills 2da + { + if(array_get_int(OBJECT_SELF, "Skills",i) != 0) // if there are points in the skill, add it to the header + { + sText+= GetStringByStrRef(StringToInt(Get2DACache("skills", "Name", i))); + sText+= " "+IntToString(array_get_int(OBJECT_SELF, "Skills",i))+"\n"; + } + } + sText += "\n" + GetStringByStrRef(16824210); // Is this correct? + SetHeader(sText); + // choices Y/N + AddChoice(GetStringByStrRef(4753), -1); // no + AddChoice(GetStringByStrRef(4752), 1); // yes + MarkStageSetUp(nStage); + break; + } + case STAGE_FEAT: { + sText = GetStringByStrRef(397) + "\n"; // Select Feats + sText += GetStringByStrRef(398) + ": "; // Feats remaining + // if it's the first time through, work out the number of feats + int nFeatsRemaining = GetLocalInt(OBJECT_SELF, "Points"); + if (!nFeatsRemaining) // first time through + { + nFeatsRemaining = 1; // always have at least 1 + // check for quick to master + nFeatsRemaining += GetLocalInt(OBJECT_SELF, "QTM"); + // set how many times to go through this stage + SetLocalInt(OBJECT_SELF, "Points", nFeatsRemaining); + // mark skill focus feat prereq here so it's only done once + // note: any other skill that is restricted to certain classes needs to be added here + // and the local ints deleted in the STAGE_BONUS_FEAT_CHECK case of HandleChoice() + // and it enforced in CheckSkillPrereq() + // UMD and animal empathy are the only ones so far + MarkSkillFocusPrereq(SKILL_ANIMAL_EMPATHY, "bHasAnimalEmpathy"); + MarkSkillFocusPrereq(SKILL_USE_MAGIC_DEVICE, "bHasUMD"); + } + // check for bonus feat(s) from class - show the player the total feats + // even though class bonuses are a different stage + int nClass = GetLocalInt(OBJECT_SELF, "Class"); + nFeatsRemaining += StringToInt(Get2DACache(Get2DACache("Classes", "BonusFeatsTable", nClass), "Bonus", 0)); + sText += IntToString(nFeatsRemaining); + SetHeader(sText); + // do feat list + SetLocalInt(OBJECT_SELF, "DynConv_Waiting", TRUE); + if (array_exists(OBJECT_SELF, "CachedChoiceTokens")) + { + // add cached choices to convo + AddChoicesFromCache(); + DeleteLocalInt(OBJECT_SELF, "DynConv_Waiting"); + } + else + { + DoFeatLoop(); + } + MarkStageSetUp(nStage); + SetDefaultTokens(); + break; + } + case STAGE_FEAT_CHECK: { + sText = GetStringByStrRef(16824209) + " "; // You have selected: + // get feat + int nFeat = array_get_int(OBJECT_SELF, "Feats", (array_get_size(OBJECT_SELF, "Feats") - 1)); + // alertness fix + if (nFeat == -1) + nFeat = 0; + sText += GetStringByStrRef(StringToInt(Get2DACache("feat", "FEAT", nFeat))) + "\n"; // name + sText += GetStringByStrRef(StringToInt(Get2DACache("feat", "DESCRIPTION", nFeat))) + "\n"; // description + sText+= "\n"+GetStringByStrRef(16824210); // Is this correct? + SetHeader(sText); + // choices Y/N + AddChoice(GetStringByStrRef(4753), -1); // no + AddChoice(GetStringByStrRef(4752), 1); // yes + MarkStageSetUp(nStage); + break; + } + case STAGE_BONUS_FEAT: { + sText = GetStringByStrRef(397) + "\n"; // Select Feats + sText += GetStringByStrRef(398) + ": "; // Feats remaining + int nFeatsRemaining = GetLocalInt(OBJECT_SELF, "Points"); + if (!nFeatsRemaining) // first time through + { + // check for bonus feat(s) from class + int nClass = GetLocalInt(OBJECT_SELF, "Class"); + nFeatsRemaining += StringToInt(Get2DACache(Get2DACache("Classes", "BonusFeatsTable", nClass), "Bonus", 0)); + // set how many times to go through this stage + SetLocalInt(OBJECT_SELF, "Points", nFeatsRemaining); + } + sText += IntToString(nFeatsRemaining); + SetHeader(sText); + // do feat list + SetLocalInt(OBJECT_SELF, "DynConv_Waiting", TRUE); + if (array_exists(OBJECT_SELF, "CachedChoiceTokens")) + { + // add cached choices to convo + AddChoicesFromCache(); + DeleteLocalInt(OBJECT_SELF, "DynConv_Waiting"); + } + else + { + DoBonusFeatLoop(); + } + MarkStageSetUp(nStage); + SetDefaultTokens(); + break; + } + case STAGE_BONUS_FEAT_CHECK: { + sText = GetStringByStrRef(16824209) + " "; // You have selected: + // get feat + int nFeat = array_get_int(OBJECT_SELF, "Feats", (array_get_size(OBJECT_SELF, "Feats") - 1)); + sText += GetStringByStrRef(StringToInt(Get2DACache("feat", "FEAT", nFeat))) + "\n"; // name + sText += GetStringByStrRef(StringToInt(Get2DACache("feat", "DESCRIPTION", nFeat))) + "\n"; // description + sText+= "\n"+GetStringByStrRef(16824210); // Is this correct? + SetHeader(sText); + // choices Y/N + AddChoice(GetStringByStrRef(4753), -1); // no + AddChoice(GetStringByStrRef(4752), 1); // yes + MarkStageSetUp(nStage); + break; + } + case STAGE_WIZ_SCHOOL: { + sText = GetStringByStrRef(381); // Select a School of Magic + SetHeader(sText); + // choices + if(GetPRCSwitch(PRC_PNP_SPELL_SCHOOLS)) + { + AddChoice(GetStringByStrRef(StringToInt(Get2DACache("spellschools", "StringRef", 9))), 9); + } + else + { + for(i = 0; i < 9; i++) + { + AddChoice(GetStringByStrRef(StringToInt(Get2DACache("spellschools", "StringRef", i))), i); + } + } + MarkStageSetUp(nStage); + break; + } + case STAGE_WIZ_SCHOOL_CHECK: { + sText = GetStringByStrRef(16824209) + " "; // You have selected: + sText+= GetStringByStrRef(StringToInt(Get2DACache("spellschools", "StringRef", GetLocalInt(OBJECT_SELF, "School")))); + sText+= "\n\n"; + sText+= GetStringByStrRef(StringToInt(Get2DACache("spellschools", "Description", GetLocalInt(OBJECT_SELF, "School")))); + sText+= "\n"+GetStringByStrRef(16824210); // Is this correct? + SetHeader(sText); + // choices Y/N + AddChoice(GetStringByStrRef(4753), -1); // no + AddChoice(GetStringByStrRef(4752), 1); // yes + MarkStageSetUp(nStage); + break; + } + case STAGE_SPELLS_0: { + // if the first time through, set up the number of cantrips to pick + int nPoints = GetLocalInt(OBJECT_SELF, "Points"); + int nClass = GetLocalInt(OBJECT_SELF, "Class"); + if(nPoints == 0) + { + // set up the number of spells to pick + // get the cls_spkn_***2da to use + string sSpkn = Get2DACache("classes", "SpellKnownTable", nClass); + // set the number of spells to pick + nPoints = StringToInt(Get2DACache(sSpkn, "SpellLevel0", 0)); + SetLocalInt(OBJECT_SELF, "Points", nPoints); + // don't want to be waiting every time + SetLocalInt(OBJECT_SELF, "DynConv_Waiting", TRUE); + } + sText = GetStringByStrRef(372) + "\n"; // Select Cantrips + sText += GetStringByStrRef(371) + ": "; // Remaining Spells + sText += IntToString(nPoints); + SetHeader(sText); + // choices, uses nStage to see if it's listing level 0 or level 1 spells + DoSpellsLoop(nStage); + MarkStageSetUp(nStage); + SetDefaultTokens(); + break; + } + case STAGE_SPELLS_1: { + // if the first time through, set up the number of spells to pick + int nPoints = GetLocalInt(OBJECT_SELF, "Points"); + int nClass = GetLocalInt(OBJECT_SELF, "Class"); + if(nPoints == 0) + { + switch(nClass) + { + case CLASS_TYPE_WIZARD: { + // spells to pick is 3 + int modifier + int nIntMod = GetLocalInt(OBJECT_SELF, "Int"); + nIntMod += StringToInt(Get2DACache("racialtypes", "IntAdjust", GetLocalInt(OBJECT_SELF, "Race"))); + nIntMod = (nIntMod - 10)/2; + nPoints = 3 + nIntMod; + break; + } + case CLASS_TYPE_SORCERER: { + // get the cls_spkn_***2da to use + string sSpkn = Get2DACache("classes", "SpellKnownTable", nClass); + // set the number of spells to pick + nPoints = StringToInt(Get2DACache(sSpkn, "SpellLevel1", 0)); + break; + } + } + SetLocalInt(OBJECT_SELF, "Points", nPoints); + // don't want to be waiting every time + SetLocalInt(OBJECT_SELF, "DynConv_Waiting", TRUE); + } + sText = GetStringByStrRef(368) + "\n"; // Select Spells for your Character + sText += GetStringByStrRef(371) + ": "; // Remaining Spells + sText += IntToString(GetLocalInt(OBJECT_SELF, "Points")); + SetHeader(sText); + // choices, uses nStage to see if it's listing level 0 or level 1 spells + DoSpellsLoop(nStage); + MarkStageSetUp(nStage); + SetDefaultTokens(); + break; + } + case STAGE_SPELLS_CHECK: { + sText = GetStringByStrRef(16824209) + "\n"; // You have selected: + int spellID = 0; + sText += GetStringByStrRef(691) + " - \n"; // Cantrips + // loop through the spell choices + for (i = 0; i < array_get_size(OBJECT_SELF, "SpellLvl0"); i++) + { + spellID = array_get_int(OBJECT_SELF, "SpellLvl0", i); + sText+= GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", spellID))); + sText += "\n"; + } + sText += GetStringByStrRef(61924) + " - \n"; // Level 1 Spells + // loop through the spell choices + for (i = 0; i < array_get_size(OBJECT_SELF, "SpellLvl1"); i++) + { + spellID = array_get_int(OBJECT_SELF, "SpellLvl1", i); + sText+= GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", spellID))); + sText += "\n"; + } + sText+= "\n"+GetStringByStrRef(16824210); // Is this correct? + SetHeader(sText); + // choices Y/N + AddChoice(GetStringByStrRef(4753), -1); // no + AddChoice(GetStringByStrRef(4752), 1); // yes + MarkStageSetUp(nStage); + break; + } + case STAGE_FAMILIAR: { + int nClass = GetLocalInt(OBJECT_SELF, "Class"); + sText = GetStringByStrRef(5607) + "\n"; // Choose a Familiar for your Character + sText += "(" + GetStringByStrRef(StringToInt(Get2DACache("classes", "Name", nClass))) + ")"; + SetHeader(sText); + SetLocalInt(OBJECT_SELF, "DynConv_Waiting", TRUE); + // do choices + if (nClass == CLASS_TYPE_DRUID) + Do2daLoop("hen_companion", "strref", GetPRCSwitch(FILE_END_ANIMALCOMP)); + else // wizard or sorc + Do2daLoop("hen_familiar", "strref", GetPRCSwitch(FILE_END_FAMILIAR)); + SetLocalInt(OBJECT_SELF, "DynConv_Waiting", FALSE); + FloatingTextStringOnCreature("Done", OBJECT_SELF, FALSE); + MarkStageSetUp(nStage); + SetDefaultTokens(); + break; + } + case STAGE_FAMILIAR_CHECK: { + sText = GetStringByStrRef(16824209) + " "; // You have selected: + if (GetLocalInt(OBJECT_SELF, "Class") == CLASS_TYPE_DRUID) + { + int nCompanion = GetLocalInt(OBJECT_SELF, "Companion"); + sText += GetStringByStrRef(StringToInt(Get2DACache("hen_companion", "STRREF", nCompanion))); + sText += "\n"; + sText += GetStringByStrRef(StringToInt(Get2DACache("hen_companion", "DESCRIPTION", nCompanion))); + sText += "\n"; + } + else + { + int nFamiliar = GetLocalInt(OBJECT_SELF, "Familiar"); + sText += GetStringByStrRef(StringToInt(Get2DACache("hen_familiar", "STRREF", nFamiliar))); + sText += "\n"; + sText += GetStringByStrRef(StringToInt(Get2DACache("hen_familiar", "DESCRIPTION", nFamiliar))); + sText += "\n"; + } + sText += "\n"+GetStringByStrRef(16824210); // Is this correct? + SetHeader(sText); + // choices Y/N + AddChoice(GetStringByStrRef(4753), -1); // no + AddChoice(GetStringByStrRef(4752), 1); // yes + MarkStageSetUp(nStage); + break; + } + case STAGE_DOMAIN: { + sText = GetStringByStrRef(5982); // Pick Cleric Domain + SetHeader(sText); + SetLocalInt(OBJECT_SELF, "DynConv_Waiting", TRUE); + // choices + DoDomainsLoop(); + MarkStageSetUp(nStage); + SetDefaultTokens(); + break; + } + case STAGE_DOMAIN_CHECK1: { + sText = GetStringByStrRef(16824209) + "\n"; // You have selected: + // first domain + int nDomain = GetLocalInt(OBJECT_SELF,"Domain1"); + // fix for air domain being 0 + if (nDomain == -1) + nDomain = 0; + sText += GetStringByStrRef(StringToInt(Get2DACache("domains", "Name", nDomain))) + "\n"; + sText += GetStringByStrRef(StringToInt(Get2DACache("domains", "Description", nDomain))) + "\n"; + sText += "\n"+GetStringByStrRef(16824210); // Is this correct? + SetHeader(sText); + // choices Y/N + AddChoice(GetStringByStrRef(4753), -1); // no + AddChoice(GetStringByStrRef(4752), 1); // yes + MarkStageSetUp(nStage); + break; + } + case STAGE_DOMAIN_CHECK2: { + sText = GetStringByStrRef(16824209) + "\n"; // You have selected: + // second domain + int nDomain = GetLocalInt(OBJECT_SELF,"Domain2"); + // fix for air domain being 0 + if (nDomain == -1) + nDomain = 0; + sText += GetStringByStrRef(StringToInt(Get2DACache("domains", "Name", nDomain))) + "\n"; + sText += GetStringByStrRef(StringToInt(Get2DACache("domains", "Description", nDomain))) + "\n"; + sText += "\n"+GetStringByStrRef(16824210); // Is this correct? + SetHeader(sText); + // choices Y/N + AddChoice(GetStringByStrRef(4753), -1); // no + AddChoice(GetStringByStrRef(4752), 1); // yes + MarkStageSetUp(nStage); + break; + } + case STAGE_APPEARANCE: { + sText = GetStringByStrRef(124); // Select the Appearance of your Character + SetHeader(sText); + SetLocalInt(OBJECT_SELF, "DynConv_Waiting", TRUE); + if(GetPRCSwitch(PRC_CONVOCC_USE_RACIAL_APPEARANCES)) // restrict to what is given in the 2da + SetupRacialAppearances(); + else + DoAppearanceLoop(); + MarkStageSetUp(nStage); + SetDefaultTokens(); + break; + } + case STAGE_APPEARANCE_CHECK: { + sText = GetStringByStrRef(16824209) + " "; // You have selected: + int nAppearance = GetLocalInt(OBJECT_SELF, "Appearance"); + int nStrRef = StringToInt(Get2DACache("appearance", "STRING_REF", nAppearance)); + if(nStrRef) + sText += GetStringByStrRef(nStrRef); + else + sText += Get2DACache("appearance", "LABEL", nAppearance); + sText += "\n"+GetStringByStrRef(16824210); // Is this correct? + SetHeader(sText); + // choices Y/N + AddChoice(GetStringByStrRef(4753), -1); // no + AddChoice(GetStringByStrRef(4752), 1); // yes + MarkStageSetUp(nStage); + break; + } + case STAGE_PORTRAIT: { + sText = GetStringByStrRef(7383); // Select a portrait + SetHeader(sText); + if(GetPRCSwitch(PRC_CONVOCC_ALLOW_TO_KEEP_PORTRAIT)) + AddChoice("Keep existing portrait.", -1); + // if(GetPRCSwitch(PRC_CONVOCC_USE_RACIAL_PORTRAIT)) + SetLocalInt(OBJECT_SELF, "DynConv_Waiting", TRUE); + DoPortraitsLoop(); + MarkStageSetUp(nStage); + SetDefaultTokens(); + break; + } + case STAGE_PORTRAIT_CHECK: { + sText = GetStringByStrRef(16824209) + " "; // You have selected: + int nPortrait = GetPortraitId(OBJECT_SELF); + sText += Get2DACache("portraits", "BaseResRef", nPortrait); + sText += "\n"+GetStringByStrRef(16824210); // Is this correct? + SetHeader(sText); + // choices Y/N + AddChoice("View this portrait.", 2); + AddChoice(GetStringByStrRef(4753), -1); // no + AddChoice(GetStringByStrRef(4752), 1); // yes + MarkStageSetUp(nStage); + break; + } + case STAGE_SOUNDSET: { + sText = GetStringByStrRef(7535); // Select a sound set + SetHeader(sText); + if(GetPRCSwitch(PRC_CONVOCC_ALLOW_TO_KEEP_VOICESET)) + AddChoice("keep existing soundset.", -1); + SetLocalInt(OBJECT_SELF, "DynConv_Waiting", TRUE); + DoSoundsetLoop(); + MarkStageSetUp(nStage); + SetDefaultTokens(); + break; + } + case STAGE_SOUNDSET_CHECK: { + sText = GetStringByStrRef(16824209) + " "; // You have selected: + int nSoundset = GetLocalInt(OBJECT_SELF, "Soundset"); + sText += GetStringByStrRef(StringToInt(Get2DACache("soundset", "STRREF", nSoundset))); + sText += "\n"+GetStringByStrRef(16824210); // Is this correct? + SetHeader(sText); + // choices Y/N + AddChoice("Listen to this soundset.", 2); + AddChoice(GetStringByStrRef(4753), -1); // no + AddChoice(GetStringByStrRef(4752), 1); // yes + MarkStageSetUp(nStage); + break; + } + case STAGE_HEAD: { + sText = GetStringByStrRef(124); // Select the Appearance of your Character + sText += "(" + GetStringByStrRef(123) + ")"; // Head + SetHeader(sText); + AddChoice("keep existing head", -1); + SetupHeadChoices(); + MarkStageSetUp(nStage); + SetDefaultTokens(); + break; + } + case STAGE_HEAD_CHECK: { + sText = "\n"+GetStringByStrRef(16824210); // Is this correct? + SetHeader(sText); + // choices Y/N + AddChoice(GetStringByStrRef(4753), -1); // no + AddChoice(GetStringByStrRef(4752), 1); // yes + MarkStageSetUp(nStage); + break; + } + case STAGE_TATTOO: { + sText = GetStringByStrRef(124); // Select the Appearance of your Character + sText += "(" + GetStringByStrRef(1591) + ")"; // Tattoo + SetHeader(sText); + AddChoice("keep current tatoos", -1); + AddChoice("torso", CREATURE_PART_TORSO); + AddChoice("right shin", CREATURE_PART_RIGHT_SHIN); + AddChoice("left shin", CREATURE_PART_LEFT_SHIN); + AddChoice("right thigh", CREATURE_PART_RIGHT_THIGH); + AddChoice("left thigh", CREATURE_PART_LEFT_THIGH); + AddChoice("right forearm", CREATURE_PART_RIGHT_FOREARM); + AddChoice("left forearm", CREATURE_PART_LEFT_FOREARM); + AddChoice("right bicep", CREATURE_PART_RIGHT_BICEP); + AddChoice("left bicep", CREATURE_PART_LEFT_BICEP); + MarkStageSetUp(nStage); + break; + } + case STAGE_TATTOO_CHECK: { + sText = "\n"+GetStringByStrRef(16824210); // Is this correct? + SetHeader(sText); + // choices Y/N + AddChoice(GetStringByStrRef(4753), -1); // no + AddChoice(GetStringByStrRef(4752), 1); // yes + MarkStageSetUp(nStage); + break; + } + case STAGE_WINGS: { + sText = GetStringByStrRef(124); // Select the Appearance of your Character + sText += "(" + GetStringByStrRef(2409) + ")"; // Wings + SetHeader(sText); + DoWingmodelLoop(); + MarkStageSetUp(nStage); + break; + } + case STAGE_WINGS_CHECK: { + sText = GetStringByStrRef(16824209) + " "; // You have selected: + int nWingType = GetCreatureWingType(); + sText += Get2DACache("wingmodel", "label", nWingType); + sText += "\n"+GetStringByStrRef(16824210); // Is this correct? + SetHeader(sText); + // choices Y/N + AddChoice(GetStringByStrRef(4753), -1); // no + AddChoice(GetStringByStrRef(4752), 1); // yes + MarkStageSetUp(nStage); + break; + } + case STAGE_TAIL: { + sText = GetStringByStrRef(124); // Select the Appearance of your Character + sText += "(" + GetStringByStrRef(2410) + ")"; // Tail + SetHeader(sText); + DoTailmodelLoop(); + MarkStageSetUp(nStage); + break; + } + case STAGE_TAIL_CHECK: { + sText = GetStringByStrRef(16824209) + " "; // You have selected: + int nTailType = GetCreatureTailType(); + sText += Get2DACache("tailmodel", "label", nTailType); + sText += "\n"+GetStringByStrRef(16824210); // Is this correct? + SetHeader(sText); + // choices Y/N + AddChoice(GetStringByStrRef(4753), -1); // no + AddChoice(GetStringByStrRef(4752), 1); // yes + MarkStageSetUp(nStage); + break; + } + case STAGE_SKIN_COLOUR: { + sText = "Pick a colour category "; + sText += "(" + GetStringByStrRef(128) + ")"; // Skin Color + SetHeader(sText); + AddChoice("Keep current skin colour", -1); + AddChoice("Tan colours", 1); + AddChoice("Sand & Rose brown", 2); + AddChoice("Tan-Greys and blues", 3); + AddChoice("Gold and obsidian", 4); + AddChoice("Greens", 5); + AddChoice("Greys and reds", 6); + AddChoice("Bright blues, greens and yellows", 7); + // new colours + AddChoice("Metallic & pure white and black", 8); + AddChoice("Smoky Group 1", 9); + AddChoice("Smoky Group 2", 10); + AddChoice("Smoky Group 3", 11); + AddChoice("Smoky Group 4", 12); + AddChoice("Black Cherry & Cinnamon", 13); + AddChoice("Hunter Green & Druid Green", 14); + AddChoice("Graveyard Fog & Chestnut", 15); + AddChoice("Clay & Toasted Ash", 16); + AddChoice("Snail Brown & Cobalt Blue", 17); + AddChoice("Midnight Blue & Peacock Green", 18); + AddChoice("Royal Purple, Mountain Blue, & Sea Foam Green", 19); + AddChoice("Spring Green, Honey Gold, Copper Coin & Berry Ice", 20); + AddChoice("Sugar Plum, Ice Blue, Black, & White", 21); + AddChoice("Greens, Mystics, & Browns", 22); + MarkStageSetUp(nStage); + SetDefaultTokens(); + break; + } + case STAGE_SKIN_COLOUR_CHOICE: { + sText = "Pick a colour "; + sText += "(" + GetStringByStrRef(128) + ")"; // Skin Color + SetHeader(sText); + int nCategory = GetLocalInt(OBJECT_SELF, "CATEGORY_SELECTED"); + AddColourChoices(nStage, nCategory); + MarkStageSetUp(nStage); + SetDefaultTokens(); + break; + } + case STAGE_SKIN_COLOUR_CHECK: { + sText = "\n"+GetStringByStrRef(16824210); // Is this correct? + SetHeader(sText); + // choices Y/N + AddChoice(GetStringByStrRef(4753), -1); // no + AddChoice(GetStringByStrRef(4752), 1); // yes + MarkStageSetUp(nStage); + break; + } + case STAGE_HAIR_COLOUR: { + sText = "Pick a colour category "; + sText += "(" + GetStringByStrRef(125) + ")"; // Hair Color + SetHeader(sText); + AddChoice("Keep current hair colour", -1); + AddChoice("Chestnuts and Reds", 1); + AddChoice("Blondes and Browns", 2); + AddChoice("White, Greys and Black", 3); + AddChoice("Blues", 4); + AddChoice("Greens", 5); + AddChoice("Spring Greens and Yellows", 6); + AddChoice("Oranges and Pinks", 7); + // new colours + AddChoice("Metallic & pure white and black", 8); + AddChoice("Smoky Group 1", 9); + AddChoice("Smoky Group 2", 10); + AddChoice("Smoky Group 3", 11); + AddChoice("Smoky Group 4", 12); + AddChoice("Black Cherry & Cinnamon", 13); + AddChoice("Hunter Green & Druid Green", 14); + AddChoice("Graveyard Fog & Chestnut", 15); + AddChoice("Clay & Toasted Ash", 16); + AddChoice("Snail Brown & Cobalt Blue", 17); + AddChoice("Midnight Blue & Peacock Green", 18); + AddChoice("Royal Purple, Mountain Blue, & Sea Foam Green", 19); + AddChoice("Spring Green, Honey Gold, Copper Coin & Berry Ice", 20); + AddChoice("Sugar Plum, Ice Blue, Black, & White", 21); + AddChoice("Greens, Mystics, & Browns", 22); + MarkStageSetUp(nStage); + SetDefaultTokens(); + break; + } + case STAGE_HAIR_COLOUR_CHOICE: { + sText = "Pick a colour "; + sText += "(" + GetStringByStrRef(125) + ")"; // Hair Color + SetHeader(sText); + int nCategory = GetLocalInt(OBJECT_SELF, "CATEGORY_SELECTED"); + AddColourChoices(nStage, nCategory); + MarkStageSetUp(nStage); + SetDefaultTokens(); + break; + } + case STAGE_HAIR_COLOUR_CHECK: { + sText = "\n"+GetStringByStrRef(16824210); // Is this correct? + SetHeader(sText); + // choices Y/N + AddChoice(GetStringByStrRef(4753), -1); // no + AddChoice(GetStringByStrRef(4752), 1); // yes + MarkStageSetUp(nStage); + break; + } + case STAGE_TATTOO1_COLOUR: { + sText = "Pick a colour category "; + sText += "(" + GetStringByStrRef(337) + ")"; // Tattoo Colors + SetHeader(sText); + AddChoice("Keep current tattoo 1 colour", -1); + AddChoice("Tan/Brown & Tan/Red", 1); + AddChoice("Tan/Yellow & Tan/Grey", 2); + AddChoice("Olive, White, Grey & Charcoal", 3); + AddChoice("Blue, Aqua, Teal & Green", 4); + AddChoice("Yellow, Orange, Red & Pink", 5); + AddChoice("Purple, Violet & Shiny/Metallic group 1", 6); + AddChoice("Shiny/Metallic group 2", 7); + // new colours + AddChoice("Metallic & pure white and black", 8); + AddChoice("Smoky Group 1", 9); + AddChoice("Smoky Group 2", 10); + AddChoice("Smoky Group 3", 11); + AddChoice("Smoky Group 4", 12); + AddChoice("Black Cherry & Cinnamon", 13); + AddChoice("Hunter Green & Druid Green", 14); + AddChoice("Graveyard Fog & Chestnut", 15); + AddChoice("Clay & Toasted Ash", 16); + AddChoice("Snail Brown & Cobalt Blue", 17); + AddChoice("Midnight Blue & Peacock Green", 18); + AddChoice("Royal Purple, Mountain Blue, & Sea Foam Green", 19); + AddChoice("Spring Green, Honey Gold, Copper Coin & Berry Ice", 20); + AddChoice("Sugar Plum, Ice Blue, Black, & White", 21); + AddChoice("Greens, Mystics, & Browns", 22); + MarkStageSetUp(nStage); + SetDefaultTokens(); + break; + } + case STAGE_TATTOO1_COLOUR_CHOICE: { + sText = "Pick a colour "; + sText += "(" + GetStringByStrRef(337) + ")"; // Tattoo Colors + SetHeader(sText); + int nCategory = GetLocalInt(OBJECT_SELF, "CATEGORY_SELECTED"); + AddColourChoices(nStage, nCategory); + MarkStageSetUp(nStage); + SetDefaultTokens(); + break; + } + case STAGE_TATTOO1_COLOUR_CHECK: { + sText = "\n"+GetStringByStrRef(16824210); // Is this correct? + SetHeader(sText); + // choices Y/N + AddChoice(GetStringByStrRef(4753), -1); // no + AddChoice(GetStringByStrRef(4752), 1); // yes + MarkStageSetUp(nStage); + break; + } + case STAGE_TATTOO2_COLOUR: { + sText = "Pick a colour category "; + sText += "(" + GetStringByStrRef(337) + ")"; // Tattoo Colors + SetHeader(sText); + AddChoice("Keep current tattoo 2 colour", -1); + AddChoice("Tan/Brown & Tan/Red", 1); + AddChoice("Tan/Yellow & Tan/Grey", 2); + AddChoice("Olive, White, Grey & Charcoal", 3); + AddChoice("Blue, Aqua, Teal & Green", 4); + AddChoice("Yellow, Orange, Red & Pink", 5); + AddChoice("Purple, Violet & Shiny/Metallic group 1", 6); + AddChoice("Shiny/Metallic group 2", 7); + // new colours + AddChoice("Metallic & pure white and black", 8); + AddChoice("Smoky Group 1", 9); + AddChoice("Smoky Group 2", 10); + AddChoice("Smoky Group 3", 11); + AddChoice("Smoky Group 4", 12); + AddChoice("Black Cherry & Cinnamon", 13); + AddChoice("Hunter Green & Druid Green", 14); + AddChoice("Graveyard Fog & Chestnut", 15); + AddChoice("Clay & Toasted Ash", 16); + AddChoice("Snail Brown & Cobalt Blue", 17); + AddChoice("Midnight Blue & Peacock Green", 18); + AddChoice("Royal Purple, Mountain Blue, & Sea Foam Green", 19); + AddChoice("Spring Green, Honey Gold, Copper Coin & Berry Ice", 20); + AddChoice("Sugar Plum, Ice Blue, Black, & White", 21); + AddChoice("Greens, Mystics, & Browns", 22); + MarkStageSetUp(nStage); + SetDefaultTokens(); + break; + } + case STAGE_TATTOO2_COLOUR_CHOICE: { + sText = "Pick a colour "; + sText += "(" + GetStringByStrRef(337) + ")"; // Tattoo Colors + SetHeader(sText); + int nCategory = GetLocalInt(OBJECT_SELF, "CATEGORY_SELECTED"); + AddColourChoices(nStage, nCategory); + MarkStageSetUp(nStage); + SetDefaultTokens(); + break; + } + case STAGE_TATTOO2_COLOUR_CHECK: { + sText = "\n"+GetStringByStrRef(16824210); // Is this correct? + SetHeader(sText); + // choices Y/N + AddChoice(GetStringByStrRef(4753), -1); // no + AddChoice(GetStringByStrRef(4752), 1); // yes + MarkStageSetUp(nStage); + break; + } + case FINAL_STAGE: { + sText = "Your character will now be generated. As part of this process, you will be booted. Please exit NWN completely before rejoining."; + SetHeader(sText); + AddChoice("Make Character", 1); + MarkStageSetUp(nStage); + // give the PC the woodsman outfit so they don't have to be naked + CreateItemOnObject("NW_CLOTH001", OBJECT_SELF); + break; + } + default: + DoDebug("ccc_inc_convo: DoHeaderAndChoices(): Unknown nStage value: " + IntToString(nStage)); + } +} + +int HandleChoice(int nStage, int nChoice) +{ + switch(nStage) + { + case STAGE_INTRODUCTION: + nStage++; + break; + + case STAGE_GENDER: + SetLocalInt(OBJECT_SELF, "Gender", nChoice); + nStage++; + break; + + case STAGE_GENDER_CHECK: { + if(nChoice == 1) + nStage++; + else // go back to pick gender + { + nStage = STAGE_GENDER; + MarkStageNotSetUp(STAGE_GENDER_CHECK, OBJECT_SELF); + MarkStageNotSetUp(STAGE_GENDER, OBJECT_SELF); + DeleteLocalInt(OBJECT_SELF, "Gender"); + } + break; + } + case STAGE_RACE: + SetLocalInt(OBJECT_SELF, "Race", nChoice); + nStage++; + break; + case STAGE_RACE_CHECK: { + if(nChoice == 1) + { + nStage++; + DoCutscene(OBJECT_SELF); + // store racial feat variables + AddRaceFeats(GetLocalInt(OBJECT_SELF, "Race")); + } + else // go back and pick race + { + nStage = STAGE_RACE; + MarkStageNotSetUp(STAGE_RACE_CHECK, OBJECT_SELF); + MarkStageNotSetUp(STAGE_RACE, OBJECT_SELF); + DeleteLocalInt(OBJECT_SELF, "Race"); + } + break; + } + case STAGE_CLASS: + SetLocalInt(OBJECT_SELF, "Class", nChoice); + nStage++; + break; + case STAGE_CLASS_CHECK: { + if(nChoice == 1) + { + nStage++; + // add class feats + AddClassFeats(GetLocalInt(OBJECT_SELF, "Class")); + // now for hitpoints (without con alteration) + SetLocalInt(OBJECT_SELF, "HitPoints", + StringToInt(Get2DACache("classes", "HitDie", + GetLocalInt(OBJECT_SELF, "Class")))); + } + else // go back and pick class + { + nStage = STAGE_CLASS; + MarkStageNotSetUp(STAGE_CLASS_CHECK, OBJECT_SELF); + MarkStageNotSetUp(STAGE_CLASS, OBJECT_SELF); + DeleteLocalInt(OBJECT_SELF, "Class"); + } + break; + } + case STAGE_ALIGNMENT: { + // for stage check later + SetLocalInt(OBJECT_SELF, "AlignChoice", nChoice); + switch(nChoice) + { + case 112: //lawful good + SetLocalInt(OBJECT_SELF, "LawfulChaotic", 85); + SetLocalInt(OBJECT_SELF, "GoodEvil", 85); + break; + case 115: //neutral good + SetLocalInt(OBJECT_SELF, "LawfulChaotic", 50); + SetLocalInt(OBJECT_SELF, "GoodEvil", 85); + break; + case 118: //chaotic good + SetLocalInt(OBJECT_SELF, "LawfulChaotic", 15); + SetLocalInt(OBJECT_SELF, "GoodEvil", 85); + break; + case 113: //lawful neutral + SetLocalInt(OBJECT_SELF, "LawfulChaotic", 85); + SetLocalInt(OBJECT_SELF, "GoodEvil", 50); + break; + case 116: //true neutral + SetLocalInt(OBJECT_SELF, "LawfulChaotic", 50); + SetLocalInt(OBJECT_SELF, "GoodEvil", 50); + break; + case 119: //chaotic neutral + SetLocalInt(OBJECT_SELF, "LawfulChaotic", 15); + SetLocalInt(OBJECT_SELF, "GoodEvil", 50); + break; + case 114: //lawful evil + SetLocalInt(OBJECT_SELF, "LawfulChaotic", 85); + SetLocalInt(OBJECT_SELF, "GoodEvil", 15); + break; + case 117: //neutral evil + SetLocalInt(OBJECT_SELF, "LawfulChaotic", 50); + SetLocalInt(OBJECT_SELF, "GoodEvil", 15); + break; + case 120: //chaotic evil + SetLocalInt(OBJECT_SELF, "LawfulChaotic", 15); + SetLocalInt(OBJECT_SELF, "GoodEvil", 15); + break; + default: + DoDebug("Duh, that clearly didn't work right"); + } + nStage++; + break; + } + case STAGE_ALIGNMENT_CHECK: { + if(nChoice == 1) + { + nStage++; + } + else // go back and pick alignment + { + nStage = STAGE_ALIGNMENT; + MarkStageNotSetUp(STAGE_ALIGNMENT_CHECK, OBJECT_SELF); + MarkStageNotSetUp(STAGE_ALIGNMENT, OBJECT_SELF); + DeleteLocalInt(OBJECT_SELF, "AlignChoice"); + DeleteLocalInt(OBJECT_SELF, "LawfulChaotic"); + DeleteLocalInt(OBJECT_SELF, "GoodEvil"); + } + break; + } + case STAGE_ABILITY: { + int nAbilityScore; + switch(nChoice) + { + case ABILITY_STRENGTH: + nAbilityScore = IncreaseAbilityScore(GetLocalInt(OBJECT_SELF, "Str")); + SetLocalInt(OBJECT_SELF, "Str", nAbilityScore); + break; + case ABILITY_DEXTERITY: + nAbilityScore = IncreaseAbilityScore(GetLocalInt(OBJECT_SELF, "Dex")); + SetLocalInt(OBJECT_SELF, "Dex", nAbilityScore); + break; + case ABILITY_CONSTITUTION: + nAbilityScore = IncreaseAbilityScore(GetLocalInt(OBJECT_SELF, "Con")); + SetLocalInt(OBJECT_SELF, "Con", nAbilityScore); + break; + case ABILITY_INTELLIGENCE: + nAbilityScore = IncreaseAbilityScore(GetLocalInt(OBJECT_SELF, "Int")); + SetLocalInt(OBJECT_SELF, "Int", nAbilityScore); + break; + case ABILITY_WISDOM: + nAbilityScore = IncreaseAbilityScore(GetLocalInt(OBJECT_SELF, "Wis")); + SetLocalInt(OBJECT_SELF, "Wis", nAbilityScore); + break; + case ABILITY_CHARISMA: + nAbilityScore = IncreaseAbilityScore(GetLocalInt(OBJECT_SELF, "Cha")); + SetLocalInt(OBJECT_SELF, "Cha", nAbilityScore); + break; + } + int nPoints = GetLocalInt(OBJECT_SELF, "Points"); // new total + if (nPoints) // if there's still points to allocate + { + // resets the stage so that the convo choices reflect the new ability scores + ClearCurrentStage(); + } + else + nStage++; // go to next stage + break; + } + case STAGE_ABILITY_CHECK:{ + if(nChoice == 1) + { + nStage++; + } + else // go back and reselect ability score + { + nStage = STAGE_ABILITY; + MarkStageNotSetUp(STAGE_ABILITY_CHECK, OBJECT_SELF); + MarkStageNotSetUp(STAGE_ABILITY, OBJECT_SELF); + DeleteLocalInt(OBJECT_SELF, "Str"); + DeleteLocalInt(OBJECT_SELF, "Dex"); + DeleteLocalInt(OBJECT_SELF, "Con"); + DeleteLocalInt(OBJECT_SELF, "Int"); + DeleteLocalInt(OBJECT_SELF, "Wis"); + DeleteLocalInt(OBJECT_SELF, "Cha"); + } + break; + } + case STAGE_SKILL: { + // first time through, create the skills array + if(!array_exists(OBJECT_SELF, "Skills")) + array_create(OBJECT_SELF, "Skills"); + // get current points + int nPoints = GetLocalInt(OBJECT_SELF, "Points"); + if(GetChoice(OBJECT_SELF) == -2) // save all remaining points + { + SetLocalInt(OBJECT_SELF, "SavedSkillPoints", nPoints); + nPoints = 0; + } + else // chosen a skill to increase + { + // get the cls_skill_*** 2da to use + string sFile = Get2DACache("classes", "SkillsTable", GetLocalInt(OBJECT_SELF, "Class")); + // work out what line in skills.2da it corresponds to + int nSkillIndex = StringToInt(Get2DAString(sFile, "SkillIndex", nChoice)); + //increase the points in that skill + // the array index is the line number in skills.2da + array_set_int(OBJECT_SELF, "Skills", nSkillIndex, + array_get_int(OBJECT_SELF, "Skills", nSkillIndex)+1); + //decrease points remaining + // see if it's class or cross-class + int nClassSkill = StringToInt(Get2DAString(sFile, "ClassSkill", nChoice)); + if (nClassSkill) // class skill + nPoints -= 1; + else // cross class skill + nPoints -= 2; + } + // store new points total + SetLocalInt(OBJECT_SELF, "Points", nPoints); + if (nPoints) // still some left to allocate + { + // Store offset so that if the user decides not to take the power, + // we can return to the same page in the power list instead of resetting to the beginning + // Store the value +1 in order to be able to differentiate between offset 0 and undefined + SetLocalInt(OBJECT_SELF, "SkillListChoiceOffset", GetLocalInt(OBJECT_SELF, DYNCONV_CHOICEOFFSET) + 1); + ClearCurrentStage(); + } + else + nStage++; + break; + } + case STAGE_SKILL_CHECK: { + if (nChoice == 1) + nStage++; + else + { + nStage = STAGE_SKILL; + MarkStageNotSetUp(STAGE_SKILL_CHECK, OBJECT_SELF); + MarkStageNotSetUp(STAGE_SKILL, OBJECT_SELF); + DeleteLocalInt(OBJECT_SELF, "SavedSkillPoints"); + DeleteLocalInt(OBJECT_SELF, "Points"); + array_delete(OBJECT_SELF, "Skills"); + } + break; + } + case STAGE_FEAT: { + int nArraySize = array_get_size(OBJECT_SELF, "Feats"); + // alertness fix + if (nChoice == 0) + nChoice = -1; + // add the feat chosen to the feat array + array_set_int(OBJECT_SELF, "Feats", array_get_size(OBJECT_SELF, "Feats"), nChoice); + nStage++; + break; + } + case STAGE_FEAT_CHECK: { + if (nChoice == 1) + { + // delete the stored convo choice list + ClearCachedChoices(); + // decrement the number of feats left to pick + int nFeatsRemaining = GetLocalInt(OBJECT_SELF, "Points"); + --nFeatsRemaining; + // check new number of feats left + if (nFeatsRemaining == 0) + { + // no more feats left to pick + // if there's a bonus feat to pick, go to next stage + nFeatsRemaining = StringToInt(Get2DACache(Get2DACache("Classes", "BonusFeatsTable", + GetLocalInt(OBJECT_SELF, "Class")), "Bonus", 0)); + if (nFeatsRemaining) + { + // go to bonus feat stage + nStage = STAGE_BONUS_FEAT; + } + else + { + // go to next stage after that the PC qualifies for + nStage = GetNextCCCStage(nStage); + } + } + else + { + // go back to feat stage to pick next feat + nStage = STAGE_FEAT; + MarkStageNotSetUp(STAGE_FEAT_CHECK, OBJECT_SELF); + MarkStageNotSetUp(STAGE_FEAT, OBJECT_SELF); + } + SetLocalInt(OBJECT_SELF, "Points", nFeatsRemaining); + } + else + { + nStage = STAGE_FEAT; + MarkStageNotSetUp(STAGE_FEAT_CHECK, OBJECT_SELF); + MarkStageNotSetUp(STAGE_FEAT, OBJECT_SELF); + // delete the chosen feat + if(array_shrink(OBJECT_SELF, "Feats", (array_get_size(OBJECT_SELF, "Feats") - 1)) != SDL_SUCCESS) + DoDebug("No feats array!"); + } + break; + } + case STAGE_BONUS_FEAT: { + int nArraySize = array_get_size(OBJECT_SELF, "Feats"); + // alertness fix + if (nChoice == 0) + nChoice = -1; + // add the feat chosen to the feat array + array_set_int(OBJECT_SELF, "Feats", array_get_size(OBJECT_SELF, "Feats"), nChoice); + nStage++; + break; + } + case STAGE_BONUS_FEAT_CHECK: { + if (nChoice == 1) + { + // delete the stored convo choice list + ClearCachedChoices(); + // decrement the number of feats left to pick + int nFeatsRemaining = GetLocalInt(OBJECT_SELF, "Points"); + --nFeatsRemaining; + // check new number of feats left + if (nFeatsRemaining == 0) + { + // no more feats left to pick + // tidy up locals + DeleteLocalInt(OBJECT_SELF, "bHasAnimalEmpathy"); + DeleteLocalInt(OBJECT_SELF, "bHasUMD"); + // go to next stage after that the PC qualifies for + nStage = GetNextCCCStage(nStage); + } + else + { + // go back to feat stage to pick next feat + nStage = STAGE_BONUS_FEAT; + MarkStageNotSetUp(STAGE_BONUS_FEAT_CHECK, OBJECT_SELF); + MarkStageNotSetUp(STAGE_BONUS_FEAT, OBJECT_SELF); + } + SetLocalInt(OBJECT_SELF, "Points", nFeatsRemaining); + } + else + { + nStage = STAGE_BONUS_FEAT; + MarkStageNotSetUp(STAGE_BONUS_FEAT_CHECK, OBJECT_SELF); + MarkStageNotSetUp(STAGE_BONUS_FEAT, OBJECT_SELF); + // delete the chosen feat + if(array_shrink(OBJECT_SELF, "Feats", (array_get_size(OBJECT_SELF, "Feats") - 1)) != SDL_SUCCESS) + DoDebug("No feats array!"); + } + break; + } + case STAGE_WIZ_SCHOOL: { + SetLocalInt(OBJECT_SELF, "School", GetChoice()); + nStage++; + break; + } + case STAGE_WIZ_SCHOOL_CHECK: { + if(nChoice == 1) + { + // go to next stage after that the PC qualifies for + nStage = GetNextCCCStage(nStage); + // add cantrips - wizards know all of them so don't need to choose + SetWizCantrips(GetLocalInt(OBJECT_SELF, "School")); + } + else // go back and pick the school again + { + nStage = STAGE_WIZ_SCHOOL; + MarkStageNotSetUp(STAGE_WIZ_SCHOOL_CHECK, OBJECT_SELF); + MarkStageNotSetUp(STAGE_WIZ_SCHOOL, OBJECT_SELF); + DeleteLocalInt(OBJECT_SELF, "School"); + } + break; + } + case STAGE_SPELLS_0: { + // create the array first time through + if (!array_exists(OBJECT_SELF, "SpellLvl0")) + array_create(OBJECT_SELF, "SpellLvl0"); + int nPoints = GetLocalInt(OBJECT_SELF, "Points"); + // add the choice to the spells known array + array_set_int(OBJECT_SELF, "SpellLvl0", array_get_size(OBJECT_SELF, "SpellLvl0"), nChoice); + // decrement the number of spells left to select + SetLocalInt(OBJECT_SELF, "Points", --nPoints); + if (nPoints) // still some left to allocate + ClearCurrentStage(); + else // go to next stage after that the PC qualifies for + nStage = GetNextCCCStage(nStage); + break; + } + case STAGE_SPELLS_1: { + // create the array first time through + if (!array_exists(OBJECT_SELF, "SpellLvl1")) + array_create(OBJECT_SELF, "SpellLvl1"); + int nPoints = GetLocalInt(OBJECT_SELF, "Points"); + // add the choice to the spells known array + array_set_int(OBJECT_SELF, "SpellLvl1", array_get_size(OBJECT_SELF, "SpellLvl1"), nChoice); + // decrement the number of spells left to select + SetLocalInt(OBJECT_SELF, "Points", --nPoints); + if (nPoints) // still some left to allocate + ClearCurrentStage(); + else // go to next stage after that the PC qualifies for + nStage = GetNextCCCStage(nStage); + break; + } + case STAGE_SPELLS_CHECK: { + if(nChoice == 1) + { + // go to next stage after that the PC qualifies for + nStage = GetNextCCCStage(nStage); + int nClass = GetLocalInt(OBJECT_SELF, "Class"); + // get the cls_spgn_***2da to use + string sSpgn = Get2DACache("classes", "SpellGainTable", nClass); + int nSpellsPerDay; + // level 1 spells + if (array_exists(OBJECT_SELF, "SpellLvl1")) + { + nSpellsPerDay = StringToInt(Get2DACache(sSpgn, "SpellLevel1", 0)); + SetLocalInt(OBJECT_SELF, "SpellsPerDay1", nSpellsPerDay); + } + // cantrips + if (array_exists(OBJECT_SELF, "SpellLvl0")) + { + nSpellsPerDay = StringToInt(Get2DACache(sSpgn, "SpellLevel0", 0)); + SetLocalInt(OBJECT_SELF, "SpellsPerDay0", nSpellsPerDay); + } + } + else // go back and pick the spells again + { + // hacky...but returns the right stage, depending on the class + nStage = GetNextCCCStage(STAGE_WIZ_SCHOOL_CHECK); + MarkStageNotSetUp(STAGE_SPELLS_CHECK, OBJECT_SELF); + MarkStageNotSetUp(STAGE_SPELLS_1, OBJECT_SELF); + MarkStageNotSetUp(STAGE_SPELLS_0, OBJECT_SELF); + DeleteLocalInt(OBJECT_SELF, "Points"); + array_delete(OBJECT_SELF, "SpellLvl1"); + if(nStage == STAGE_SPELLS_0) + { + // if the new value of nStage takes us back to picking cantrips, + // then also delete the level 0 array + array_delete(OBJECT_SELF, "SpellLvl0"); + } + } + break; + } + case STAGE_FAMILIAR: { + if(GetLocalInt(OBJECT_SELF, "Class") == CLASS_TYPE_DRUID) + SetLocalInt(OBJECT_SELF, "Companion", nChoice); + else // sorc or wiz + SetLocalInt(OBJECT_SELF, "Familiar", nChoice); + nStage++; + break; + } + case STAGE_FAMILIAR_CHECK: { + if (nChoice == 1) + nStage = GetNextCCCStage(nStage); + else + { + nStage = STAGE_FAMILIAR; + MarkStageNotSetUp(STAGE_FAMILIAR_CHECK, OBJECT_SELF); + MarkStageNotSetUp(STAGE_FAMILIAR, OBJECT_SELF); + DeleteLocalInt(OBJECT_SELF, "Familiar"); + DeleteLocalInt(OBJECT_SELF, "Companion"); + } + break; + } + case STAGE_DOMAIN: { + // if this is the first domain chosen + if (GetLocalInt(OBJECT_SELF, "Domain1") == 0) + { + // fix for air domain being 0 + if (nChoice == 0) + nChoice = -1; + SetLocalInt(OBJECT_SELF, "Domain1", nChoice); + nStage = STAGE_DOMAIN_CHECK1; + } + else // second domain + { + // fix for air domain being 0 + if (nChoice == 0) + nChoice = -1; + SetLocalInt(OBJECT_SELF, "Domain2", nChoice); + nStage = STAGE_DOMAIN_CHECK2; + } + break; + } + case STAGE_DOMAIN_CHECK1: { + if (nChoice == 1) + { + nStage = STAGE_DOMAIN; + MarkStageNotSetUp(STAGE_DOMAIN_CHECK1); + MarkStageNotSetUp(STAGE_DOMAIN); + } + else + { + nStage = STAGE_DOMAIN; + MarkStageNotSetUp(STAGE_DOMAIN_CHECK1); + MarkStageNotSetUp(STAGE_DOMAIN); + DeleteLocalInt(OBJECT_SELF,"Domain1"); + DeleteLocalInt(OBJECT_SELF,"Domain2"); + } + break; + } + case STAGE_DOMAIN_CHECK2: { + if (nChoice == 1) + { + nStage++; + // add domain feats + AddDomainFeats(); + } + else + { + nStage = STAGE_DOMAIN; + MarkStageNotSetUp(STAGE_DOMAIN_CHECK2); + MarkStageNotSetUp(STAGE_DOMAIN); + DeleteLocalInt(OBJECT_SELF,"Domain1"); + DeleteLocalInt(OBJECT_SELF,"Domain2"); + } + break; + } + case STAGE_APPEARANCE: { + if (nChoice == -1) // no change + { + SetLocalInt(OBJECT_SELF, "Appearance", GetAppearanceType(OBJECT_SELF)); + nStage = STAGE_PORTRAIT; + } + else + { + SetLocalInt(OBJECT_SELF, "Appearance", nChoice); + SetCreatureAppearanceType(OBJECT_SELF, nChoice); + // change the appearance + DoCutscene(OBJECT_SELF); + nStage++; + } + break; + } + case STAGE_APPEARANCE_CHECK: { + if (nChoice == 1) + { + nStage++; + } + else + { + nStage = STAGE_APPEARANCE; + MarkStageNotSetUp(STAGE_APPEARANCE_CHECK, OBJECT_SELF); + MarkStageNotSetUp(STAGE_APPEARANCE, OBJECT_SELF); + DeleteLocalInt(OBJECT_SELF, "Appearance"); + } + break; + } + case STAGE_PORTRAIT: { + if (nChoice == -1) // no change + { + nStage = STAGE_SOUNDSET; + } + else + { + // change the portrait + SetPortraitId(OBJECT_SELF, nChoice); + // change the clone's portrait + object oClone = GetLocalObject(OBJECT_SELF, "Clone"); + SetPortraitId(oClone, nChoice); + nStage++; + } + break; + } + case STAGE_PORTRAIT_CHECK: { + if (nChoice == 2) + { + object oClone = GetLocalObject(OBJECT_SELF, "Clone"); + ActionExamine(oClone); + // DelayCommand(1.0, ActionExamine(oClone)); + // DelayCommand(2.0, ActionExamine(oClone)); + } + else if (nChoice == 1) + { + nStage++; + } + else + { + nStage = STAGE_PORTRAIT; + MarkStageNotSetUp(STAGE_PORTRAIT_CHECK, OBJECT_SELF); + MarkStageNotSetUp(STAGE_PORTRAIT, OBJECT_SELF); + } + break; + } + case STAGE_SOUNDSET: { + if (nChoice == -1) // no change + { + SetLocalInt(OBJECT_SELF, "Soundset", nChoice); + nStage = GetNextCCCStage(nStage, FALSE); + } + else + { + // store the choice + SetLocalInt(OBJECT_SELF, "Soundset", nChoice); + // modify the clone + DoCutscene(OBJECT_SELF); + nStage++; + } + break; + } + case STAGE_SOUNDSET_CHECK: { + if (nChoice == 2) + { + object oClone = GetLocalObject(OBJECT_SELF, "Clone"); + PlayVoiceChat(0 , oClone); + } + else if (nChoice == 1) + { + // set up colour defaults here to make sure they don't reset for non-player type appearances + SetLocalInt(OBJECT_SELF, "Skin", -1); + SetLocalInt(OBJECT_SELF, "Hair", -1); + SetLocalInt(OBJECT_SELF, "TattooColour1", -1); + SetLocalInt(OBJECT_SELF, "TattooColour2", -1); + nStage = GetNextCCCStage(nStage, FALSE); + } + else + { + nStage = STAGE_SOUNDSET; + MarkStageNotSetUp(STAGE_SOUNDSET_CHECK, OBJECT_SELF); + MarkStageNotSetUp(STAGE_SOUNDSET, OBJECT_SELF); + DeleteLocalInt(OBJECT_SELF, "Soundset"); + } + break; + } + case STAGE_HEAD: { + if (nChoice == -1) // no change + { + nStage = GetNextCCCStage(nStage, FALSE); + } + else + { + // change the head + SetCreatureBodyPart(CREATURE_PART_HEAD, nChoice); + // modify the clone's head + object oClone = GetLocalObject(OBJECT_SELF, "Clone"); + SetCreatureBodyPart(CREATURE_PART_HEAD, nChoice, oClone); + nStage++; + } + break; + } + case STAGE_HEAD_CHECK: { + if (nChoice == 1) + { + nStage = GetNextCCCStage(nStage, FALSE); + } + else + { + nStage = STAGE_HEAD; + MarkStageNotSetUp(STAGE_HEAD_CHECK, OBJECT_SELF); + MarkStageNotSetUp(STAGE_HEAD, OBJECT_SELF); + } + break; + } + case STAGE_TATTOO: { + if (nChoice == -1) // no change + { + nStage = GetNextCCCStage(nStage, FALSE); + } + else + { + int nTattooed = GetCreatureBodyPart(nChoice, OBJECT_SELF); + if(nTattooed == 1) + nTattooed = 2; + else if(nTattooed == 2) + nTattooed = 1; + // change the tattoo on the clone + SetCreatureBodyPart(nChoice, nTattooed, + GetLocalObject(OBJECT_SELF, "Clone")); + // change the tattoo on the PC + SetCreatureBodyPart(nChoice, nTattooed); + } + break; + } + case STAGE_TATTOO_CHECK: { + if (nChoice == 1) + { + nStage = GetNextCCCStage(nStage, FALSE); + } + else + { + nStage = STAGE_TATTOO; + MarkStageNotSetUp(STAGE_TATTOO_CHECK, OBJECT_SELF); + MarkStageNotSetUp(STAGE_TATTOO, OBJECT_SELF); + } + break; + } + case STAGE_WINGS: { + SetCreatureWingType(nChoice); + // alter the clone + object oClone = GetLocalObject(OBJECT_SELF, "Clone"); + SetCreatureWingType(nChoice, oClone); + nStage++; + break; + } + case STAGE_WINGS_CHECK: { + if (nChoice == 1) + { + nStage = GetNextCCCStage(nStage, FALSE); + } + else + { + nStage = STAGE_WINGS; + MarkStageNotSetUp(STAGE_WINGS_CHECK, OBJECT_SELF); + MarkStageNotSetUp(STAGE_WINGS, OBJECT_SELF); + } + break; + } + case STAGE_TAIL: { + SetCreatureTailType(nChoice); + // alter the clone + object oClone = GetLocalObject(OBJECT_SELF, "Clone"); + SetCreatureTailType(nChoice, oClone); + nStage++; + break; + } + case STAGE_TAIL_CHECK: { + if (nChoice == 1) + { + nStage = GetNextCCCStage(nStage, FALSE); + } + else + { + nStage = STAGE_TAIL; + MarkStageNotSetUp(STAGE_TAIL_CHECK, OBJECT_SELF); + MarkStageNotSetUp(STAGE_TAIL, OBJECT_SELF); + } + break; + } + case STAGE_SKIN_COLOUR: { + if (nChoice == -1) // keep existing + { + nStage = STAGE_SKIN_COLOUR_CHECK; + SetLocalInt(OBJECT_SELF, "Skin", -1); + } + else + { + SetLocalInt(OBJECT_SELF, "CATEGORY_SELECTED", nChoice); + nStage++; + } + break; + } + case STAGE_SKIN_COLOUR_CHOICE: { + SetLocalInt(OBJECT_SELF, "Skin", nChoice); + // change the clone + DoCutscene(OBJECT_SELF); + nStage++; + break; + } + case STAGE_SKIN_COLOUR_CHECK: { + if (nChoice == 1) + { + nStage++; + } + else + { + nStage = STAGE_SKIN_COLOUR; + MarkStageNotSetUp(STAGE_SKIN_COLOUR_CHECK, OBJECT_SELF); + MarkStageNotSetUp(STAGE_SKIN_COLOUR_CHOICE, OBJECT_SELF); + MarkStageNotSetUp(STAGE_SKIN_COLOUR, OBJECT_SELF); + DeleteLocalInt(OBJECT_SELF, "Skin"); + } + DeleteLocalInt(OBJECT_SELF, "CATEGORY_SELECTED"); + break; + } + case STAGE_HAIR_COLOUR: { + if (nChoice == -1) // keep existing + { + nStage = STAGE_HAIR_COLOUR_CHECK; + SetLocalInt(OBJECT_SELF, "Hair", -1); + } + else + { + SetLocalInt(OBJECT_SELF, "CATEGORY_SELECTED", nChoice); + nStage++; + } + break; + } + case STAGE_HAIR_COLOUR_CHOICE: { + SetLocalInt(OBJECT_SELF, "Hair", nChoice); + // change the clone + DoCutscene(OBJECT_SELF); + nStage++; + break; + } + case STAGE_HAIR_COLOUR_CHECK: { + if (nChoice == 1) + { + nStage++; + } + else + { + nStage = STAGE_HAIR_COLOUR; + MarkStageNotSetUp(STAGE_HAIR_COLOUR_CHECK, OBJECT_SELF); + MarkStageNotSetUp(STAGE_HAIR_COLOUR_CHOICE, OBJECT_SELF); + MarkStageNotSetUp(STAGE_HAIR_COLOUR, OBJECT_SELF); + DeleteLocalInt(OBJECT_SELF, "Hair"); + } + DeleteLocalInt(OBJECT_SELF, "CATEGORY_SELECTED"); + break; + } + case STAGE_TATTOO1_COLOUR: { + if (nChoice == -1) // keep existing + { + nStage = STAGE_TATTOO1_COLOUR_CHECK; + SetLocalInt(OBJECT_SELF, "TattooColour1", -1); + } + else + { + SetLocalInt(OBJECT_SELF, "CATEGORY_SELECTED", nChoice); + nStage++; + } + break; + } + case STAGE_TATTOO1_COLOUR_CHOICE: { + SetLocalInt(OBJECT_SELF, "TattooColour1", nChoice); + // change the clone + DoCutscene(OBJECT_SELF); + nStage++; + break; + } + case STAGE_TATTOO1_COLOUR_CHECK: { + if (nChoice == 1) + { + nStage++; + } + else + { + nStage = STAGE_TATTOO1_COLOUR; + MarkStageNotSetUp(STAGE_TATTOO1_COLOUR_CHECK, OBJECT_SELF); + MarkStageNotSetUp(STAGE_TATTOO1_COLOUR_CHOICE, OBJECT_SELF); + MarkStageNotSetUp(STAGE_TATTOO1_COLOUR, OBJECT_SELF); + DeleteLocalInt(OBJECT_SELF, "TattooColour1"); + } + DeleteLocalInt(OBJECT_SELF, "CATEGORY_SELECTED"); + break; + } + case STAGE_TATTOO2_COLOUR: { + if (nChoice == -1) // keep existing + { + nStage = STAGE_TATTOO2_COLOUR_CHECK; + SetLocalInt(OBJECT_SELF, "TattooColour2", -1); + } + else + { + SetLocalInt(OBJECT_SELF, "CATEGORY_SELECTED", nChoice); + nStage++; + } + break; + } + case STAGE_TATTOO2_COLOUR_CHOICE: { + SetLocalInt(OBJECT_SELF, "TattooColour2", nChoice); + // change the clone + DoCutscene(OBJECT_SELF); + nStage++; + break; + } + case STAGE_TATTOO2_COLOUR_CHECK: { + if (nChoice == 1) + { + nStage = FINAL_STAGE; + } + else + { + nStage = STAGE_TATTOO2_COLOUR; + MarkStageNotSetUp(STAGE_TATTOO2_COLOUR_CHECK, OBJECT_SELF); + MarkStageNotSetUp(STAGE_TATTOO2_COLOUR_CHOICE, OBJECT_SELF); + MarkStageNotSetUp(STAGE_TATTOO2_COLOUR, OBJECT_SELF); + DeleteLocalInt(OBJECT_SELF, "TattooColour2"); + } + DeleteLocalInt(OBJECT_SELF, "CATEGORY_SELECTED"); + break; + } + case FINAL_STAGE: { + ExecuteScript("prc_ccc_make_pc", OBJECT_SELF); + AllowExit(DYNCONV_EXIT_FORCE_EXIT); + break; + } + } + return nStage; +} diff --git a/nwn/nwnprc/_removed/ccc_inc_misc.nss b/nwn/nwnprc/_removed/ccc_inc_misc.nss new file mode 100644 index 00000000..7a779281 --- /dev/null +++ b/nwn/nwnprc/_removed/ccc_inc_misc.nss @@ -0,0 +1,2372 @@ +/** + * convoCC misc functions + */ + +#include "inc_utility" +#include "prc_alterations" +#include "inc_dynconv" +#include "prc_ccc_const" +#include "inc_sql" + +// checks if it's a multiplayer game and that letoscript is set up correctly +// returns 0 for pass and 1 for fail +int DoLetoscriptTest(object oPC); + +// makes a string based on the PC name and player public CD key +string Encrypt(object oPC); + +// removes all equipped items from the PC +// that means ALL items, even plot ones +void DoStripPC(object oPC); + +// checks if oPC is valid and in an area then boots them +void CheckAndBoot(object oPC); + +// 'nice' version of CheckAndBoot() that gives a warning and a 5 second countdown +void CheckAndBootNicely(object oPC); + +/** + * main cutscene function + * letoscript changes to the PC clone are done via this function + * nSetup indicates whether the cutscene needs seting up or not + */ +void DoCutscene(object oPC, int nSetup = FALSE); + +/** + * Cutscene pseudo HB functions + */ + +// used to cleanup clones when a player leaves +void CloneMasterCheck(); + +// sets up the camera to rotate around the clone +// and the clone do random animations +void DoRotatingCamera(object oPC); + +/** + * functions to set appearance, portrait, soundset + */ + +// sets race appearance as defined in racialtype.2da +// removes wings, tails and undead graft arm as well as making invisible bits visible +void DoSetRaceAppearance(object oPC); + +// assigns the ccc chosen gender to the clone and resets the soundset +// if it's changed +void DoCloneGender(object oPC); + +// changes the appearance of the PC and clone +void DoSetAppearance(object oPC); + +// deletes local variables stored on the PC before the player is booted to make the new character +void DoCleanup(); + +// set up the ability choice in " (racial <+/-modifier>) . Cost to increase " format +void AddAbilityChoice(int nStatValue, string sStatName, string sRacialAdjust, int nAbilityConst); + +// subtracts the correct amount of points for increasing the current ability score +// returns the current ability score incremented by 1 +int IncreaseAbilityScore(int nCurrentAbilityScore); + +//this returns the cost to get to a score +//or the cost saved by dropping from that score +int GetCost(int nAbilityScore); + +// this marks if the PC qualifies for skill focus feats that are class specific +void MarkSkillFocusPrereq(int nSkill, string sVarName); + +// works out the next stage to go to between STAGE_FEAT_CHECK and STAGE_APPEARANCE +// when given the stage just completed +// covers caster and familiar related choices +int GetNextCCCStage(int nStage, int nSpellCasterStage = TRUE); + +// adds a reply choice to the cache array +// used to store a set of dynamic conversation choices so they don't have to be +// unnecessarily regenerated from the database +void AddCachedChoice(string sText, int nValue, object oPC = OBJECT_SELF); + +// delete the stored convo choice cache array +void ClearCachedChoices(object oPC = OBJECT_SELF); + +// sets the convoCC's current choice list to the values stored in the cache array +void AddChoicesFromCache(object oPC = OBJECT_SELF); + +// checks if the PC has the two feats given as arguements +// '****' as an arguement is treated as an automatic TRUE for that arguement +// used to check if the PC meets the prerequisites in the PreReqFeat1 AND PreReqFeat2 +// columns of feat.2da +int GetMeetsANDPreReq(string sPreReqFeat1, string sPreReqFeat2); + +// checks if the PC has any one of the 5 feats given as arguements +// '****' as an arguement is treated as an automatic TRUE for that arguement +// used to check if the PC meets the prerequisites in the OrReqFeat0 OR OrReqFeat1 +// OR OrReqFeat2 OR OrReqFeat3 OR OrReqFeat4 columns of feat.2da +int GetMeetsORPreReq(string sOrReqFeat0, string sOrReqFeat1, string sOrReqFeat2, string sOrReqFeat3, string sOrReqFeat4); + +// loops through the feat array on the PC to see if it has nReqFeat +int PreReqFeatArrayLoop(int nReqFeat); + +// checks if the PC has enough skill ranks in the two skills +// '****' as ReqSkill arguement is treated as an automatic TRUE for that arguement +// used to check if the PC meets the prerequisites in the ReqSkill AND ReqSkill2 +// columns of feat.2da +int GetMeetSkillPrereq(string sReqSkill, string sReqSkill2, string sReqSkillRanks, string sReqSkillRanks2); + +// checks if the PC has enough points in sReqSkill +int CheckSkillPrereq(string sReqSkill, string sReqSkillRanks); + +// adds all the cantrips to a wizard's spellbook +void SetWizCantrips(int iSpellschool); + +// loops through the spell array on the PC to see if they already know the spell +// returns TRUE if they know it +int GetIsSpellKnown(int nSpell, int nSpellLevel); + +// sets up the appearance choices if the PRC_CONVOCC_USE_RACIAL_APPEARANCES switch is set +void SetupRacialAppearances(); + +// adds appearance choices on being passed an APPEARANCE_TYPE_* constant +void AddAppearanceChoice(int nType, int nOnlyChoice = FALSE); + +// adds the head choices based on race and gender +void SetupHeadChoices(); + +// maps the appearance to a bioware playable race +int MapAppearanceToRace(int nAppearance); + +/* 2da cache functions */ + +// loops through a 2da, using the cache +// s2da is the name of the 2da, sColumnName the column to read the values +// from, nFileEnd the number of lines in the 2da +void Do2daLoop(string s2da, string sColumnName, int nFileEnd); + +// loops through racialtypes.2da +void DoRacialtypesLoop(); + +// loops through classes.2da +void DoClassesLoop(); + +// loops through skills.2da +void DoSkillsLoop(); + +// loops through feat.2da +void DoFeatLoop(int nClassFeatStage = FALSE); + +// loops through cls_feat_***.2da +void DoBonusFeatLoop(); + +// loops through spells.2da +void DoSpellsLoop(int nStage); + +// loops through domains.2da +void DoDomainsLoop(); + +// loops through appearance.2da +void DoAppearanceLoop(); + +// loops through portraits.2da +void DoPortraitsLoop(int nGenderSort = TRUE); + +// loops through soundset.2da +void DoSoundsetLoop(int nGenderSort = TRUE); + +// loops through wingmodel.2da +void DoWingmodelLoop(); + +// loops through tailmodel.2da +void DoTailmodelLoop(); + +// stores the feats found in race_feat_***.2da as an array on the PC +void AddRaceFeats(int nRace); + +// stores the feats found in cls_feat_***.2da in the feat array on the PC +void AddClassFeats(int nClass); + +// stores the feat listed in domais.2da in the feat array on the PC +void AddDomainFeats(); + +// loops through colours.2da depending on the stage for which column it uses. +void AddColourChoices(int nStage, int nCategory); + +/* function definitions */ + +int DoLetoscriptTest(object oPC) +{ + int bBoot; + //check that its a multiplayer game + if(GetPCPublicCDKey(oPC) == "") + { + SendMessageToPC(oPC, "This module must be hosted as a multiplayer game with NWNX and Letoscript"); + WriteTimestampedLogEntry("This module must be hosted as a multiplayer game with NWNX and Letoscript"); + bBoot = TRUE; + } + + //check that letoscript is setup correctly + string sScript; + if(GetPRCSwitch(PRC_LETOSCRIPT_PHEONIX_SYNTAX)) + sScript = LetoGet("FirstName")+" "+LetoGet("LastName"); + else + sScript = LetoGet("FirstName")+"print ' ';"+LetoGet("LastName"); + + StackedLetoScript(sScript); + RunStackedLetoScriptOnObject(oPC, "LETOTEST", "SCRIPT", "", FALSE); + string sResult = GetLocalString(GetModule(), "LetoResult"); + string sName = GetName(oPC); + if(( sResult != sName + && sResult != sName+" " + && sResult != " "+sName) + ) + { + SendMessageToPC(oPC, "Error: Letoscript is not setup correctly or it cannot find your bic file. Check nwnx_leto.log for error messages."); + WriteTimestampedLogEntry("Error: Letoscript is not setup correctly or it cannot find your bic file. Check nwnx_leto.log for error messages."); + bBoot = TRUE; + } + + return bBoot; +} + +string Encrypt(object oPC) +{ + string sName = GetName(oPC); + int nKey = GetPRCSwitch(PRC_CONVOCC_ENCRYPTION_KEY); + if(nKey == 0) + nKey = 10; + string sReturn; + + string sPublicCDKey = GetPCPublicCDKey(oPC); + int nKeyTotal; + int i; + for(i=1;i STAGE_APPEARANCE && nSetup)) + { + DoSetAppearance(oPC); + } + + if(nStage == STAGE_SOUNDSET || (nStage > STAGE_SOUNDSET && nSetup)) + { + int nSoundset = GetLocalInt(oPC, "Soundset"); + if (nSoundset != -1) // then it has been changed + { + sScript += LetoSet("SoundSetFile", IntToString(nSoundset), "word"); + } + } + + if (nStage == STAGE_SKIN_COLOUR_CHOICE || (nStage > STAGE_SKIN_COLOUR_CHOICE && nSetup)) + { + int nSkin = GetLocalInt(oPC, "Skin"); + if (nSkin != -1) // then it has been changed + { + SetColor(oClone, COLOR_CHANNEL_SKIN, nSkin); + //sScript += SetSkinColor(nSkin); + } + } + + if (nStage == STAGE_HAIR_COLOUR_CHOICE || (nStage > STAGE_HAIR_COLOUR_CHOICE && nSetup)) + { + int nHair = GetLocalInt(oPC, "Hair"); + if (nHair != -1) // then it has been changed + { + SetColor(oClone, COLOR_CHANNEL_HAIR, nHair); + //sScript += SetHairColor(nHair); + } + } + + if (nStage == STAGE_TATTOO1_COLOUR_CHOICE || (nStage > STAGE_TATTOO1_COLOUR_CHOICE && nSetup)) + { + int nTattooColour1 = GetLocalInt(oPC, "TattooColour1"); + if (nTattooColour1 != -1) // then it has been changed + { + SetColor(oClone, COLOR_CHANNEL_TATTOO_1, nTattooColour1); + //sScript += SetTattooColor(nTattooColour1, 1); + } + } + + if (nStage == STAGE_TATTOO2_COLOUR_CHOICE || (nStage > STAGE_TATTOO2_COLOUR_CHOICE && nSetup)) + { + int nTattooColour2 = GetLocalInt(oPC, "TattooColour2"); + if (nTattooColour2 != -1) // then it has been changed + { + SetColor(oClone, COLOR_CHANNEL_TATTOO_2, nTattooColour2); + //sScript += SetTattooColor(nTattooColour2, 2); + } + } + // no point in running the letoscript commands if no changes are made + if (sScript != "") + { + StackedLetoScript(sScript); + string sResult; + if (oClone == OBJECT_INVALID) + oClone = GetLocalObject(oPC, "Clone"); + RunStackedLetoScriptOnObject(oClone, "OBJECT", "SPAWN", "prc_ccc_app_lspw", TRUE); + sResult = GetLocalString(GetModule(), "LetoResult"); + SetLocalObject(GetModule(), "PCForThread"+sResult, OBJECT_SELF); + } + } + // DoRotatingCamera(oPC); +} + +void CloneMasterCheck() +{ + object oMaster = GetLocalObject(OBJECT_SELF, "Master"); + if(!GetIsObjectValid(oMaster)) + { + // free up the convoCC if they logged out + DeleteLocalObject(GetModule(), "ccc_active_pc"); + SetIsDestroyable(TRUE); + DestroyObject(OBJECT_SELF); + } + else + DelayCommand(10.0, CloneMasterCheck()); +} + +void DoRotatingCamera(object oPC) +{ + if (DEBUG) DoDebug("Running DoRotatingCamera()"); + if(!GetIsObjectValid(oPC)) + { + // then the ccc is free to use again + DeleteLocalInt(GetModule(), "ccc_active_pc"); + if (DEBUG) DoDebug("Invalid PC given to DoRotatingCamera()"); + return; + } + if(GetLocalInt(oPC, "StopRotatingCamera")) + { + DeleteLocalInt(oPC, "StopRotatingCamera"); + DeleteLocalFloat(oPC, "DoRotatingCamera"); + return; + } + float fDirection = GetLocalFloat(oPC, "DoRotatingCamera"); + fDirection += 30.0; + if(fDirection > 360.0) + fDirection -= 360.0; + if(fDirection <= 0.0) + fDirection += 360.0; + SetLocalFloat(oPC, "DoRotatingCamera", fDirection); + SetCameraMode(oPC, CAMERA_MODE_TOP_DOWN); + SetCameraFacing(fDirection, 2.0, 45.0, CAMERA_TRANSITION_TYPE_VERY_SLOW); + DelayCommand(6.0, DoRotatingCamera(oPC)); + //its the clone not the PC that does things + object oClone = GetLocalObject(oPC, "Clone"); + if(GetIsObjectValid(oClone)) + oPC = oClone; + if(d2()==1) + AssignCommand(oPC, ActionPlayAnimation(100+Random(17))); + else + AssignCommand(oPC, ActionPlayAnimation(100+Random(21), 1.0, 6.0)); +} + +void DoCleanup() +{ + object oPC = OBJECT_SELF; + // go through the ones used to make the character + // delete some ints + DeleteLocalInt(oPC, "Str"); + DeleteLocalInt(oPC, "Dex"); + DeleteLocalInt(oPC, "Con"); + DeleteLocalInt(oPC, "Int"); + DeleteLocalInt(oPC, "Wis"); + DeleteLocalInt(oPC, "Cha"); + + DeleteLocalInt(oPC, "Race"); + + DeleteLocalInt(oPC, "Class"); + DeleteLocalInt(oPC, "HitPoints"); + + DeleteLocalInt(oPC, "Gender"); + + DeleteLocalInt(oPC, "LawfulChaotic"); + DeleteLocalInt(oPC, "GoodEvil"); + + DeleteLocalInt(oPC, "Familiar"); + DeleteLocalInt(oPC, "Companion"); + + DeleteLocalInt(oPC, "Domain1"); + DeleteLocalInt(oPC, "Domain2"); + + DeleteLocalInt(oPC, "School"); + + DeleteLocalInt(oPC, "SpellsPerDay0"); + DeleteLocalInt(oPC, "SpellsPerDay1"); + + DeleteLocalInt(oPC, "Soundset"); + DeleteLocalInt(oPC, "Skin"); + DeleteLocalInt(oPC, "Hair"); + DeleteLocalInt(oPC, "TattooColour1"); + DeleteLocalInt(oPC, "TattooColour2"); + + // delete some arrays + array_delete(oPC, "spellLvl0"); + array_delete(oPC, "spellLvl1"); + array_delete(oPC, "Feats"); + array_delete(oPC, "Skills"); +} + + +void AddAbilityChoice(int nAbilityScore, string sAbilityName, string sRacialAdjust, int nAbilityConst) +{ + // if it is still possible to increase the ability score, add that choice + if (nAbilityScore < GetLocalInt(OBJECT_SELF, "MaxStat") && GetLocalInt(OBJECT_SELF, "Points") >= GetCost(nAbilityScore + 1)) + { + AddChoice(sAbilityName + " " + IntToString(nAbilityScore) + " (Racial " + sRacialAdjust + "). " + + GetStringByStrRef(137) + " " + IntToString(GetCost(nAbilityScore + 1)), nAbilityConst); + } +} + +int IncreaseAbilityScore(int nCurrentAbilityScore) +{ + int nPoints = GetLocalInt(OBJECT_SELF, "Points"); + // get cost and remove from total + // note: because of how GetCost() works, the ability score is incremented here not later + nPoints -= GetCost(++nCurrentAbilityScore); + // store the total points left on the PC + SetLocalInt(OBJECT_SELF, "Points", nPoints); + return nCurrentAbilityScore; +} + +int GetCost(int nAbilityScore) +{ + int nCost = (nAbilityScore-11)/2; + if(nCost < 1) + nCost = 1; + return nCost; +} + +void MarkSkillFocusPrereq(int nSkill, string sVarName) +{ + string sFile = GetStringLowerCase(Get2DACache("classes", "SkillsTable", GetLocalInt(OBJECT_SELF, "Class"))); + // query to see if nSkill is on the cls_skill*** list + string sSkill = IntToString(nSkill); + string sSQL = "SELECT data FROM prc_cached2da WHERE file = '" + sFile + "' AND columnid= 'skillindex' AND " + +"data = '" + sSkill + "'"; + + PRC_SQLExecDirect(sSQL); + if (PRC_SQLFetch() == PRC_SQL_SUCCESS) + SetLocalInt(OBJECT_SELF, sVarName, TRUE); +} + +int GetNextCCCStage(int nStage, int nSpellCasterStage = TRUE) +{ + // check we're in the right place + if (nStage < STAGE_FEAT_CHECK) + return -1; // sent here too early + // get some info + int nClass = GetLocalInt(OBJECT_SELF, "Class"); + if(nSpellCasterStage) + { + switch (nStage) // with no breaks to go all the way through + { + case STAGE_FEAT_CHECK: { + if(nClass == CLASS_TYPE_WIZARD) + { + return STAGE_WIZ_SCHOOL; + } + } + case STAGE_WIZ_SCHOOL_CHECK: { + if (nClass == CLASS_TYPE_SORCERER || nClass == CLASS_TYPE_BARD) + { + return STAGE_SPELLS_0; + } + else if (nClass == CLASS_TYPE_WIZARD) + { + return STAGE_SPELLS_1; + } + } + case STAGE_SPELLS_0: { + if (nClass == CLASS_TYPE_SORCERER || nClass == CLASS_TYPE_BARD) + { + string sSpkn = Get2DACache("classes", "SpellKnownTable", nClass); + // if they can pick level 1 spells + if (StringToInt(Get2DACache(sSpkn, "SpellLevel1", 0))) + return STAGE_SPELLS_1; + } + } + case STAGE_SPELLS_1: { + if (nClass == CLASS_TYPE_WIZARD || nClass == CLASS_TYPE_SORCERER || nClass == CLASS_TYPE_BARD) + { + return STAGE_SPELLS_CHECK; // checks both 0 and 1 level spells + } + } + case STAGE_SPELLS_CHECK: { + if (nClass == CLASS_TYPE_WIZARD || nClass == CLASS_TYPE_SORCERER || nClass == CLASS_TYPE_DRUID) + { + return STAGE_FAMILIAR; // also does animal companion + } + } + case STAGE_FAMILIAR_CHECK: { + if (nClass == CLASS_TYPE_CLERIC) + { + return STAGE_DOMAIN; + } + } + default: + return STAGE_APPEARANCE; + } + } + else + { + int nAppearance = GetLocalInt(OBJECT_SELF, "Appearance"); + // the model type determines if the model is dynamic, can have wings or tails + string sModelType = Get2DACache("appearance", "MODELTYPE", nAppearance); + switch(nStage) // with no breaks to go all the way through + { + case STAGE_SOUNDSET: + case STAGE_SOUNDSET_CHECK: + { + if (sModelType == "P") + return STAGE_HEAD; + } + case STAGE_HEAD: + case STAGE_HEAD_CHECK: + { + if (sModelType == "P") + return STAGE_TATTOO; + } + case STAGE_TATTOO: + case STAGE_TATTOO_CHECK: + { + if (sModelType == "P" || TestStringAgainstPattern("**W**", sModelType)) + return STAGE_WINGS; + } + case STAGE_WINGS_CHECK: + { + if (sModelType == "P" || TestStringAgainstPattern("**T**", sModelType)) + return STAGE_TAIL; + } + case STAGE_TAIL_CHECK: + { + if (sModelType == "P") + return STAGE_SKIN_COLOUR; + } + default: + return FINAL_STAGE; + } + } + return -1; // silly compiler +} + +void AddCachedChoice(string sText, int nValue, object oPC = OBJECT_SELF) +{ + // pretty much the same as the AddChoice() function + if(!array_exists(oPC, "CachedChoiceTokens")) + array_create(oPC, "CachedChoiceTokens"); + if(!array_exists(oPC, "CachedChoiceValues")) + array_create(oPC, "CachedChoiceValues"); + array_set_string(oPC, "CachedChoiceTokens", array_get_size(oPC, "CachedChoiceTokens"), sText); + array_set_int (oPC, "CachedChoiceValues", array_get_size(oPC, "CachedChoiceValues"), nValue); +} + +void ClearCachedChoices(object oPC = OBJECT_SELF) +{ + array_delete(oPC, "CachedChoiceTokens"); + array_delete(oPC, "CachedChoiceValues"); +} + +void AddChoicesFromCache(object oPC = OBJECT_SELF) +{ + int nArraySize = array_get_size(oPC, "CachedChoiceTokens"); + int i, nValue; + string sText; + for(i = 0; i < nArraySize; i++) + { + sText = array_get_string(oPC, "CachedChoiceTokens", i); + nValue = array_get_int(oPC, "CachedChoiceValues", i); + AddChoice(sText, nValue); + } +} + +int GetMeetsANDPreReq(string sPreReqFeat1, string sPreReqFeat2) +{ + // are there any prereq? + if (sPreReqFeat1 == "****" && sPreReqFeat2 == "****") + return TRUE; + // test if the PC meets the first prereq + // if not, exit + if(!PreReqFeatArrayLoop(StringToInt(sPreReqFeat1))) + return FALSE; + // got this far, then the first prereq was met + // is there a second prereq? If not, done + if (sPreReqFeat2 == "****") + return TRUE; + // test if the PC meets the second one + if(!PreReqFeatArrayLoop(StringToInt(sPreReqFeat2))) + return FALSE; + // got this far, so second one matched too + return TRUE; +} + +int GetMeetsORPreReq(string sOrReqFeat0, string sOrReqFeat1, string sOrReqFeat2, string sOrReqFeat3, string sOrReqFeat4) +{ + // are there any prereq + if (sOrReqFeat0 == "****") + return TRUE; + // first one + if(PreReqFeatArrayLoop(StringToInt(sOrReqFeat0))) + return TRUE; + // second one + if(PreReqFeatArrayLoop(StringToInt(sOrReqFeat1))) + return TRUE; + // third one + if(PreReqFeatArrayLoop(StringToInt(sOrReqFeat2))) + return TRUE; + // 4th one + if(PreReqFeatArrayLoop(StringToInt(sOrReqFeat3))) + return TRUE; + // 5th one + if(PreReqFeatArrayLoop(StringToInt(sOrReqFeat4))) + return TRUE; + // no match + return FALSE; +} + +int PreReqFeatArrayLoop(int nOrReqFeat) +{ + // as alertness is stored in the array as -1 + if (nOrReqFeat == 0) + nOrReqFeat = -1; + int i = 0; + while (i != array_get_size(OBJECT_SELF, "Feats")) + { + int nFeat = array_get_int(OBJECT_SELF, "Feats", i); + if(nFeat == nOrReqFeat) // if there's a match, the prereq are met + return TRUE; + i++; + } + // otherwise no match + return FALSE; +} + +int GetMeetSkillPrereq(string sReqSkill, string sReqSkill2, string sReqSkillRanks, string sReqSkillRanks2) +{ + if(sReqSkill == "****" && sReqSkill2 == "****") + return TRUE; + // test if the PC meets the first prereq + if(!CheckSkillPrereq(sReqSkill, sReqSkillRanks)) + return FALSE; + + // got this far, then the first prereq was met + // is there a second prereq? If not, done + if(sReqSkill2 == "****") + return TRUE; + if(!CheckSkillPrereq(sReqSkill2, sReqSkillRanks2)) + return FALSE; + // got this far, so second one matched too + return TRUE; +} + +int CheckSkillPrereq(string sReqSkill, string sReqSkillRanks) +{ + // for skill focus feats + if (sReqSkillRanks == "0" || sReqSkillRanks == "****") // then it just requires being able to put points in the skill + { + // if requires animal empathy, but the PC can't take ranks in it + if(sReqSkill == "0" && !GetLocalInt(OBJECT_SELF, "bHasAnimalEmpathy")) + return FALSE; + // if requires UMD, but the PC can't take ranks in it + if(sReqSkill == IntToString(SKILL_USE_MAGIC_DEVICE) && !GetLocalInt(OBJECT_SELF, "bHasUMD")) + return FALSE; + } + else // test if the PC has enough ranks in the skill + { + int nSkillPoints = array_get_int(OBJECT_SELF, "Skills", StringToInt(sReqSkill)); + if (nSkillPoints < StringToInt(sReqSkillRanks)) + return FALSE; + } + // get this far then not failed any of the prereq + return TRUE; +} + +void SetWizCantrips(int iSpellschool) +{ + string sOpposition = ""; + // if not a generalist + if(iSpellschool) + { + sOpposition = Get2DACache("spellschools", "Letter", StringToInt(Get2DACache("spellschools", "Opposition", iSpellschool))); + } + + array_create(OBJECT_SELF, "SpellLvl0"); + string sSQL = "SELECT rowid FROM prc_cached2da_spells WHERE (wiz_sorc = '0') AND (school != '"+sOpposition+"')"; + PRC_SQLExecDirect(sSQL); + while(PRC_SQLFetch() == PRC_SQL_SUCCESS) + { + int nRow = StringToInt(PRC_SQLGetData(1)); + array_set_int(OBJECT_SELF, "SpellLvl0", array_get_size(OBJECT_SELF, "SpellLvl0"),nRow); + } +} + +int GetIsSpellKnown(int nSpell, int nSpellLevel) +{ + // spell 0 is a level 6 spell, so no need to do the 0 == -1 workaround + int i = 0; + string sArray = "SpellLvl" + IntToString(nSpellLevel); + // if the array doesn't exist then there won't be a match + if (!array_exists(OBJECT_SELF, sArray)) + return FALSE; + while (i != array_get_size(OBJECT_SELF, sArray)) + { + int nKnownSpell = array_get_int(OBJECT_SELF, sArray, i); + if(nKnownSpell == nSpell) // if there's a match, don't add it + return TRUE; + i++; + } + // otherwise no match + return FALSE; +} + +void SetupRacialAppearances() +{ + int nRace = GetLocalInt(OBJECT_SELF, "Race"); + int nSex = GetLocalInt(OBJECT_SELF, "Gender"); + string sSex = nSex == 0 ? "male" : "female"; + object oPC = OBJECT_SELF; + + // see if the PC's race and gender combination has an entry in racialappearances.2da + /* + SELECT appearance.data FROM prc_cached2da, prc_cached2da AS appearance, prc_cached2da AS gender + WHERE prc_cached2da.rowid = appearance.rowid + AND prc_cached2da.rowid = gender.rowid + AND prc_cached2da.file = "racialappearance" AND appearance.file = "racialappearance" AND gender.file = "racialappearance" + AND prc_cached2da.columnid = "Race" AND appearance.columnid = "Appearance" AND gender.columnid = "Gender" + AND prc_cached2da.data = "" AND (gender.data = "" OR gender.data = "2"); + */ + + /* + string sSQL = "SELECT appearance.data FROM prc_cached2da, prc_cached2da AS appearance, prc_cached2da AS gender " + + "WHERE prc_cached2da.rowid = appearance.rowid AND prc_cached2da.rowid = gender.rowid " + + "AND prc_cached2da.file = 'racialappearance' AND appearance.file = 'racialappearance' AND gender.file = 'racialappearance' " + + "AND prc_cached2da.columnid = 'race' AND appearance.columnid = 'appearance' AND gender.columnid = 'gender' " + + "AND prc_cached2da.data = " + IntToString(nRace) + " AND (gender.data = " + IntToString(nSex) + " OR gender.data = '2')"; + */ + string sSQL = "SELECT data FROM prc_cached2da WHERE file = 'racialappear' AND rowid = "+IntToString(nRace)+ + " AND (columnid = '"+sSex+"1' OR columnid = '"+sSex+"2' OR columnid = '"+sSex+"3' OR columnid = '"+sSex+"4' OR columnid = '"+sSex+"5')"; + + PRC_SQLExecDirect(sSQL); + int i; + array_create(oPC, "AppearanceChoices"); + while(PRC_SQLFetch() == PRC_SQL_SUCCESS) + { + string sTemp = PRC_SQLGetData(1); + if(sTemp != "****") + { + i = StringToInt(sTemp); // appearance + // as doing a Get2DACache() call here will overwrite the sql result, add to an array to do this later + array_set_int (oPC, "AppearanceChoices", array_get_size(oPC, "AppearanceChoices"), i); + } + } + // if there's any values in the array use those for choices + int nNumberOfChoices = array_get_size(oPC, "AppearanceChoices"); + if (nNumberOfChoices) + { + // loop through the array and add all the choices + i = 0; + while (i < nNumberOfChoices) + { + AddAppearanceChoice(array_get_int(oPC, "AppearanceChoices", i)); + i++; + } + } + else // it is the appearance given at the race stage + { + // the only 'choice' + AddAppearanceChoice(StringToInt(Get2DACache("racialtypes", "Appearance", nRace)), TRUE); + } + // tidy up + array_delete(oPC, "AppearanceChoices"); + DeleteLocalInt(OBJECT_SELF, "DynConv_Waiting"); +} + +void AddAppearanceChoice(int nType, int nOnlyChoice = FALSE) +{ + // get the appearance type name + string sName; + int nStrRef = StringToInt(Get2DACache("appearance", "STRING_REF", nType)); + if(nStrRef) + sName = GetStringByStrRef(nStrRef); + else + sName = Get2DACache("appearance", "LABEL", nType); + // if there's only one option, it has already been stored on the PC + // at the race choice stage + if (nOnlyChoice) + { + // add a "continue" so player doesn't expect a choice + sName = GetStringByStrRef(39) + " (" + sName + ")"; + // marker to skip the check stage + nType = -1; + } + AddChoice(sName, nType); +} + +void SetupHeadChoices() +{ + int nGender = GetLocalInt(OBJECT_SELF, "Gender"); + int nAppearance = GetLocalInt(OBJECT_SELF, "Appearance"); + // determine the number of heads (based on both appearance and gender) + int nHeadNumber; + int nHead2Start; // some appearances have a second run of head numbers + int nHead2End; + // change numbers to account for custom heads here + if(GetPRCSwitch(MARKER_CEP2)) // add in the extra (consecutive) heads + { + if(nAppearance == APPEARANCE_TYPE_HUMAN + || nAppearance == APPEARANCE_TYPE_HALF_ELF) + { + if(nGender == GENDER_MALE) + { + nHeadNumber = 49; + nHead2Start = 100; + nHead2End = 113; + } + else if (nGender == GENDER_FEMALE) + { + nHeadNumber = 49; + nHead2Start = 100; + nHead2End = 114; + } + } + else if (nAppearance == APPEARANCE_TYPE_ELF) + { + if(nGender == GENDER_MALE) + nHeadNumber = 34; + else if (nGender == GENDER_FEMALE) + { + nHeadNumber = 43; + nHead2Start = 179; + nHead2End = 180; + } + } + else if (nAppearance == APPEARANCE_TYPE_HALFLING) + { + if(nGender == GENDER_MALE) + { + nHeadNumber = 26; + nHead2Start = 160; + nHead2End = 161; + } + else if (nGender == GENDER_FEMALE) + { + nHeadNumber = 15; + nHead2Start = 161; + nHead2End = 167; + } + } + else if (nAppearance == APPEARANCE_TYPE_HALF_ORC) + { + if(nGender == GENDER_MALE) + nHeadNumber = 31; + else if (nGender == GENDER_FEMALE) + nHeadNumber = 15; + } + else if (nAppearance == APPEARANCE_TYPE_DWARF) + { + if(nGender == GENDER_MALE) + nHeadNumber = 24; + else if (nGender == GENDER_FEMALE) + nHeadNumber = 23; + } + else if(nAppearance == APPEARANCE_TYPE_GNOME) + { + if(nGender == GENDER_MALE) + nHeadNumber = 35; + else if (nGender == GENDER_FEMALE) + nHeadNumber = 10; + } + else if (nAppearance == APPEARANCE_TYPE_CEP_BROWNIE) + { + if(nGender == GENDER_MALE) + nHeadNumber = 12; + else if (nGender == GENDER_FEMALE) + nHeadNumber = 11; + } + else if (nAppearance == APPEARANCE_TYPE_CEP_WEMIC) + { + if (nGender == GENDER_MALE) + nHeadNumber = 4; + else if (nGender == GENDER_FEMALE) + nHeadNumber = 2; + } + } + else + { + if(nAppearance == APPEARANCE_TYPE_HUMAN + || nAppearance == APPEARANCE_TYPE_HALF_ELF) + { + if(nGender == GENDER_MALE) + { + nHeadNumber = 34; + nHead2Start = 140; + nHead2End = 143; + } + else if (nGender == GENDER_FEMALE) + { + nHeadNumber = 27; + nHead2Start = 140; + nHead2End = 143; + } + } + else if (nAppearance == APPEARANCE_TYPE_ELF) + { + if(nGender == GENDER_MALE) + nHeadNumber = 18; + else if (nGender == GENDER_FEMALE) + nHeadNumber = 16; + } + else if (nAppearance == APPEARANCE_TYPE_HALFLING) + { + if(nGender == GENDER_MALE) + nHeadNumber = 10; + else if (nGender == GENDER_FEMALE) + nHeadNumber = 11; + } + else if (nAppearance == APPEARANCE_TYPE_HALF_ORC) + { + if(nGender == GENDER_MALE) + nHeadNumber = 13; + else if (nGender == GENDER_FEMALE) + nHeadNumber = 12; + } + else if (nAppearance == APPEARANCE_TYPE_DWARF) + { + if(nGender == GENDER_MALE) + nHeadNumber = 13; + else if (nGender == GENDER_FEMALE) + nHeadNumber = 12; + } + else if(nAppearance == APPEARANCE_TYPE_GNOME) + { + if(nGender == GENDER_MALE) + nHeadNumber = 13; + else if (nGender == GENDER_FEMALE) + nHeadNumber = 9; + } + } + int i; + for(i=1;i<= nHeadNumber;i++) + AddChoice(IntToString(i), i); + if (nHead2Start) + { + for(i = nHead2Start;i <= nHead2End;i++) + AddChoice(IntToString(i), i); + } + + // and the non consecutive heads for the CEP + if((nAppearance == APPEARANCE_TYPE_HUMAN + || nAppearance == APPEARANCE_TYPE_HALF_ELF) && GetPRCSwitch(MARKER_CEP2)) + { + if(nGender == GENDER_MALE) + { + AddChoice(IntToString(140), 140); + AddChoice(IntToString(141), 141); + AddChoice(IntToString(142), 142); + AddChoice(IntToString(143), 143); + AddChoice(IntToString(155), 155); + } + else if (nGender == GENDER_FEMALE) + { + AddChoice(IntToString(140), 140); + AddChoice(IntToString(141), 141); + AddChoice(IntToString(142), 142); + AddChoice(IntToString(143), 143); + AddChoice(IntToString(147), 147); + AddChoice(IntToString(148), 148); + AddChoice(IntToString(149), 149); + AddChoice(IntToString(150), 150); + AddChoice(IntToString(151), 151); + AddChoice(IntToString(152), 152); + AddChoice(IntToString(155), 155); + AddChoice(IntToString(180), 180); + AddChoice(IntToString(181), 181); + } + } +} + +int MapAppearanceToRace(int nAppearance) +{ + switch(nAppearance) + { + case APPEARANCE_TYPE_DWARF: + return RACIAL_TYPE_DWARF; + break; + case APPEARANCE_TYPE_ELF: + return RACIAL_TYPE_ELF; + break; + case APPEARANCE_TYPE_GNOME: + return RACIAL_TYPE_GNOME; + break; + case APPEARANCE_TYPE_HALFLING: + return RACIAL_TYPE_HALFLING; + break; + case APPEARANCE_TYPE_HALF_ORC: + return RACIAL_TYPE_HALFORC; + break; + case APPEARANCE_TYPE_HUMAN: + case APPEARANCE_TYPE_HALF_ELF: // half-elves get human portraits + return RACIAL_TYPE_HUMAN; + break; + default: + return -1; + } + return -1; // silly compiler +} + +void Do2daLoop(string s2da, string sColumnName, int nFileEnd) +{ + /* SELECT statement + * SELECT rowid, data FROM prc_cached2da + * WHERE file = AND columnid = AND rowid >= + */ + string sSQL = "SELECT rowid, data FROM prc_cached2da WHERE file = '"+ s2da +"' AND columnid = '" + sColumnName +"' AND rowid <= " + IntToString(nFileEnd); + PRC_SQLExecDirect(sSQL); + int i; + string sData; + while(PRC_SQLFetch() == PRC_SQL_SUCCESS) + { + i = StringToInt(PRC_SQLGetData(1)); // rowid + sData = GetStringByStrRef(StringToInt(PRC_SQLGetData(2))); // data + AddChoice(sData, i); + + } +} + +void DoRacialtypesLoop() +{ + // get the results 25 rows at a time to avoid TMI + int nReali = GetLocalInt(OBJECT_SELF, "i"); + /* SELECT statement + SELECT rowid, Name FROM prc_cached2da_racialtypes + WHERE PlayerRace = 1 + LIMIT 25 OFFSET + */ + string sSQL = "SELECT rowid, name FROM prc_cached2da_racialtypes WHERE (playerrace = 1) LIMIT 25 OFFSET "+IntToString(nReali); + PRC_SQLExecDirect(sSQL); + // to keep track of where in the 25 rows we stop getting a result + int nCounter = 0; + while(PRC_SQLFetch() == PRC_SQL_SUCCESS) + { + nCounter++; + int nRace = StringToInt(PRC_SQLGetData(1)); // rowid + int bIsTakeable = TRUE; + + // check for right drow gender IF the switch is set + if(GetPRCSwitch(PRC_CONVOCC_DROW_ENFORCE_GENDER)) + { + if(nRace == RACIAL_TYPE_DROW_FEMALE + && GetLocalInt(OBJECT_SELF, "Gender") == GENDER_MALE) + bIsTakeable = FALSE; + if(nRace == RACIAL_TYPE_DROW_MALE + && GetLocalInt(OBJECT_SELF, "Gender") == GENDER_FEMALE) + bIsTakeable = FALSE; + } + + // add the choices, choice number is race rowid/constant value + if(bIsTakeable) + { + string sName = GetStringByStrRef(StringToInt(PRC_SQLGetData(2))); + AddChoice(sName, nRace); + } + } + + // IF there were 25 rows, carry on + if(nCounter == 25) + { + SetLocalInt(OBJECT_SELF, "i", nReali+25); + DelayCommand(0.01, DoRacialtypesLoop()); + } + else // there were less than 25 rows, it's the end of the 2da + { + FloatingTextStringOnCreature("Done", OBJECT_SELF, FALSE); + DeleteLocalInt(OBJECT_SELF, "DynConv_Waiting"); + DeleteLocalInt(OBJECT_SELF, "i"); + return; + } +} + +void DoClassesLoop() +{ + // remove if decide not to make the convo wait + if(GetLocalInt(OBJECT_SELF, "DynConv_Waiting") == FALSE) + return; + // get the results 25 rows at a time to avoid TMI + int nReali = GetLocalInt(OBJECT_SELF, "i"); + /* + SELECT `rowid`, `PreReqTable` FROM `prc_cached2da_classes` + WHERE (`PlayerClass` = 1) AND (`XPPenalty` = 1) + LIMIT 25 OFFSET + */ + string sSQL = "SELECT rowid, prereqtable FROM prc_cached2da_classes WHERE (playerclass = 1) AND (xppenalty = 1) LIMIT 25 OFFSET "+IntToString(nReali); + PRC_SQLExecDirect(sSQL); + // to keep track of where in the 25 rows we stop getting a result + int nCounter = 0; + int nClass; + string sPreReq, sReqType, sParam1, sParam2; + // this needs storing in a temp array because the 2da cache retrieval will clear + // the query above if both steps are done in the same loop + // two parallel arrays as there's no struct arrays + array_create(OBJECT_SELF, "temp_classes"); + array_create(OBJECT_SELF, "temp_class_prereq"); + while(PRC_SQLFetch() == PRC_SQL_SUCCESS) + { + nCounter++; + nClass = StringToInt(PRC_SQLGetData(1)); // rowid + array_set_int(OBJECT_SELF, "temp_classes", array_get_size(OBJECT_SELF, "temp_classes"), nClass); + sPreReq = PRC_SQLGetData(2); // PreReq tablename + array_set_string(OBJECT_SELF, "temp_class_prereq", array_get_size(OBJECT_SELF, "temp_class_prereq"), sPreReq); + + } + + // loop through the temp array to check for banned classes + int i; + for (i=0; i < array_get_size(OBJECT_SELF, "temp_classes"); i++) + { + nClass = array_get_int(OBJECT_SELF, "temp_classes", i); // class + sPreReq = array_get_string(OBJECT_SELF, "temp_class_prereq", i); // prereq table name + int j = 0; + // check if this base class is allowed + do + { + sReqType = Get2DACache(sPreReq, "ReqType", j); + if (sReqType == "VAR") // if we've found the class allowed variable + { + sParam1 = Get2DACache(sPreReq, "ReqParam1", j); + if(!GetLocalInt(OBJECT_SELF, sParam1)) // if the class is allowed + { + // adds the class to the choice list + string sName = GetStringByStrRef(StringToInt(Get2DACache("classes", "Name", nClass))); + AddChoice(sName, nClass); + } + } // end of if (sReqType == "VAR") + j++; + + } while (sReqType != "VAR"); // terminates as soon as we get the allowed variable + + } // end of for loop + // clean up + array_delete(OBJECT_SELF, "temp_classes"); + array_delete(OBJECT_SELF, "temp_class_prereq"); + + // IF there were 25 rows, carry on + if(nCounter == 25) + { + SetLocalInt(OBJECT_SELF, "i", nReali+25); + DelayCommand(0.01, DoClassesLoop()); + } + else // there were less than 25 rows, it's the end of the 2da + { + FloatingTextStringOnCreature("Done", OBJECT_SELF, FALSE); + DeleteLocalInt(OBJECT_SELF, "DynConv_Waiting"); + DeleteLocalInt(OBJECT_SELF, "i"); + return; + } +} + +void DoSkillsLoop() +{ + if(GetPRCSwitch(PRC_CONVOCC_ALLOW_SKILL_POINT_ROLLOVER)) + { + // add the "store" option + AddChoice("Store all remaining points.", -2); + } + // get the class of the PC + int nClass = GetLocalInt(OBJECT_SELF, "Class"); + // get the cls_skill_*** 2da to use + string sFile = GetStringLowerCase(Get2DACache("classes", "SkillsTable", nClass)); + string sSQL = "SELECT rowid, data FROM prc_cached2da WHERE file = '" + sFile + "' AND columnid= 'skillindex'"; + PRC_SQLExecDirect(sSQL); + + while(PRC_SQLFetch() == PRC_SQL_SUCCESS) + { + int nRow = StringToInt(PRC_SQLGetData(1)); + int nSkillID = StringToInt(PRC_SQLGetData(2)); // line of skills.2da for the skill + int nPoints = GetLocalInt(OBJECT_SELF, "Points"); + // get the skill name + string sName = GetStringByStrRef(StringToInt(Get2DACache("skills", "Name", nSkillID))); + // class skill or truespeak and PC is a truenamer + if(Get2DAString(sFile, "ClassSkill", nRow) == "1" || (nSkillID == SKILL_TRUESPEAK && nClass == CLASS_TYPE_TRUENAMER)) + { + sName += " " + GetStringByStrRef(52951); // (Class Skill) + // check there's not already 4 points in there + int nStoredPoints = array_get_int(OBJECT_SELF, "Skills", nSkillID); + if (nStoredPoints < 4) // level (ie 1) + 3 + { + sName += " : "+IntToString(nStoredPoints); + // only add if there's less than the maximum points allowed + AddChoice(sName, nRow); // uses cls_skill_*** rowid as choice number + } + } + else // cross-class skill + { + // check there's not already 2 points in there + int nStoredPoints = array_get_int(OBJECT_SELF, "Skills", nSkillID); + // if there's only 1 point left, then no cross class skills + if (nStoredPoints < 2 && nPoints > 1) // level (ie 1) + 1 + { + sName += " : "+IntToString(nStoredPoints); + // only add if there's less than the maximum points allowed + AddChoice(sName, nRow); // uses cls_skill_*** rowid as choice number + } + } + } + // if the dynamic convo is set waiting (first time through only) + // then mark as done + if (GetLocalInt(OBJECT_SELF, "DynConv_Waiting")) + { + FloatingTextStringOnCreature("Done", OBJECT_SELF, FALSE); + DeleteLocalInt(OBJECT_SELF, "DynConv_Waiting"); + } +} + +void DoFeatLoop(int nClassFeatStage = FALSE) +{ + /* TODO - scripting feat enforcement */ + string sSQL; + object oPC = OBJECT_SELF; + + // get the information needed to work out if the prereqs are met + int nSex = GetLocalInt(oPC, "Gender"); + int nRace = GetLocalInt(oPC, "Race"); + int nClass = GetLocalInt(oPC, "Class"); + int nStr = GetLocalInt(oPC, "Str"); + int nDex = GetLocalInt(oPC, "Dex"); + int nCon = GetLocalInt(oPC, "Con"); + int nInt = GetLocalInt(oPC, "Int"); + int nWis = GetLocalInt(oPC, "Wis"); + int nCha = GetLocalInt(oPC, "Cha"); + int nOrder = GetLocalInt(oPC, "LawfulChaotic"); + int nMoral = GetLocalInt(oPC, "GoodEvil"); + + //add racial ability alterations + nStr += StringToInt(Get2DACache("racialtypes", "StrAdjust", nRace)); + nDex += StringToInt(Get2DACache("racialtypes", "DexAdjust", nRace)); + nCon += StringToInt(Get2DACache("racialtypes", "ConAdjust", nRace)); + nInt += StringToInt(Get2DACache("racialtypes", "IntAdjust", nRace)); + nWis += StringToInt(Get2DACache("racialtypes", "WisAdjust", nRace)); + nCha += StringToInt(Get2DACache("racialtypes", "ChaAdjust", nRace)); + + // get BAB + int nBAB = StringToInt(Get2DACache(Get2DACache("classes", "AttackBonusTable", nClass), "BAB", 0)); + + // get fortitude save + int nFortSave = StringToInt(Get2DACache(Get2DACache("classes","SavingThrowTable" , nClass), "FortSave", 0)); + + // get the results 5 rows at a time to avoid TMI + int nReali = GetLocalInt(OBJECT_SELF, "i"); + + if (!nClassFeatStage) // select the general feats + { + /* + SELECT `rowid`, `feat`, `PREREQFEAT1`, `PREREQFEAT2`, `OrReqFeat0`, `OrReqFeat1`, `OrReqFeat2`, `OrReqFeat3`, `OrReqFeat4`, + `REQSKILL`, `REQSKILL2`, `ReqSkillMinRanks`, `ReqSkillMinRanks2` + FROM `prc_cached2da_feat` + WHERE (`feat` != '****') AND (`PreReqEpic` != 1) + AND (`MinLevel` = '****' OR `MinLevel` = '1') + AND `ALLCLASSESCANUSE` = 1 + AND `minattackbonus` <= + AND `minspelllvl` <= 1 + AND `minstr`<= + AND `mindex`<= + AND `mincon`<= + AND `minint`<= + AND `minwis`<= + AND `mincha`<= + AND `MinFortSave` <= + */ + + sSQL = "SELECT rowid, feat, prereqfeat1, prereqfeat2, orreqfeat0, orreqfeat1, orreqfeat2, orreqfeat3, orreqfeat4, " + +"reqskill, reqskill2, reqskillminranks, reqskillminranks2 FROM prc_cached2da_feat" + +" WHERE (feat != '****') AND (prereqepic != 1)" + +" AND (minlevel = '****' OR minlevel = '1')" + +" AND (allclassescanuse = 1)" + +" AND (minattackbonus <= "+IntToString(nBAB)+")" + +" AND (minspelllvl <= 1)" + +" AND (minstr <= "+IntToString(nStr)+")" + +" AND (mindex <= "+IntToString(nDex)+")" + +" AND (mincon <= "+IntToString(nCon)+")" + +" AND (minint <= "+IntToString(nInt)+")" + +" AND (minwis <= "+IntToString(nWis)+")" + +" AND (mincha <= "+IntToString(nCha)+")" + +" AND (minfortsave <= "+IntToString(nFortSave)+")" + +" LIMIT 5 OFFSET "+IntToString(nReali); + } + else // select the class feats + { + // get which cls_feat_*** 2da to use + string sFile = GetStringLowerCase(Get2DACache("classes", "FeatsTable", nClass)); + + /* + SELECT prc_cached2da_cls_feat.FeatIndex, prc_cached2da_cls_feat.FEAT, + prc_cached2da_feat.PREREQFEAT1, prc_cached2da_feat.PREREQFEAT2, + prc_cached2da_feat.OrReqFeat0, prc_cached2da_feat.OrReqFeat1, prc_cached2da_feat.OrReqFeat2, prc_cached2da_feat.OrReqFeat3, prc_cached2da_feat.OrReqFeat4, + prc_cached2da_feat.REQSKILL, prc_cached2da_feat.REQSKILL2, + prc_cached2da_feat.ReqMinSkillRanks, prc_cached2da_feat.ReqMinSkillRanks2 + FROM prc_cached2da_cls_feat INNER JOIN prc_cached2da_feat + WHERE (prc_cached2da_feat.FEAT != '****') AND (prc_cached2da_cls_feat.FeatIndex != '****') + AND (prc_cached2da_cls_feat.file = '') + AND (prc_cached2da_cls_feat.List <= 1) + AND (prc_cached2da_cls_feat.GrantedOnLevel <= 1) + AND (prc_cached2da_feat.rowid = prc_cached2da_cls_feat.FeatIndex) + AND (`PreReqEpic` != 1) + AND (`MinLevel` = '****' OR `MinLevel` = '1') + AND `ALLCLASSESCANUSE` = 0 + AND `minattackbonus` <= + AND `minspelllvl` <= 1 + AND `minstr`<= + AND `mindex`<= + AND `mincon`<= + AND `minint`<= + AND `minwis`<= + AND `mincha`<= + AND `MinFortSave` <= + */ + + sSQL = "SELECT prc_cached2da_feat.rowid, feat, prereqfeat1, prereqfeat2, " + +"orreqfeat0, orreqfeat1, orreqfeat2, orreqfeat3, orreqfeat4, " + +"reqskill, reqskill2, reqskillminranks, reqskillminranks2" + +" FROM prc_cached2da_feat INNER JOIN prc_cached2da_cls_feat" + +" WHERE (prc_cached2da_feat.feat != '****') AND (prc_cached2da_cls_feat.featindex != '****')" + +" AND (prc_cached2da_cls_feat.file = '" + sFile + "')" + +" AND (prc_cached2da_cls_feat.list <= 1)" + +" AND (prc_cached2da_cls_feat.grantedonlevel <= 1)" + +" AND (prc_cached2da_feat.rowid = prc_cached2da_cls_feat.featindex)" + +" AND (prereqepic != 1)" + +" AND (minlevel = '****' OR minlevel = '1')" + +" AND (allclassescanuse != 1)" + +" AND (minattackbonus <= "+IntToString(nBAB)+")" + +" AND (minspelllvl <= 1)" + +" AND (minstr <= "+IntToString(nStr)+")" + +" AND (mindex <= "+IntToString(nDex)+")" + +" AND (mincon <= "+IntToString(nCon)+")" + +" AND (minint <= "+IntToString(nInt)+")" + +" AND (minwis <= "+IntToString(nWis)+")" + +" AND (mincha <= "+IntToString(nCha)+")" + +" AND (minfortsave <= "+IntToString(nFortSave)+")" + +" LIMIT 5 OFFSET "+IntToString(nReali); + } + + // debug print the sql statement + if(DEBUG) + { + DoDebug(sSQL); + } + + PRC_SQLExecDirect(sSQL); + // to keep track of where in the 25 rows we stop getting a result + int nCounter = 0; + while(PRC_SQLFetch() == PRC_SQL_SUCCESS) + { + nCounter++; + int nRow = StringToInt(PRC_SQLGetData(1)); + if((nRow > 5320 && nRow < 7700) + || (nRow > 7938 && nRow < 23519) + || (nRow > 23601 && nRow < 24000)) + continue; + + int nStrRef = StringToInt(PRC_SQLGetData(2)); + string sName = GetStringByStrRef(nStrRef); + string sPreReqFeat1 = PRC_SQLGetData(3); + string sPreReqFeat2 = PRC_SQLGetData(4); + string sOrReqFeat0 = PRC_SQLGetData(5); + string sOrReqFeat1 = PRC_SQLGetData(6); + string sOrReqFeat2 = PRC_SQLGetData(7); + string sOrReqFeat3 = PRC_SQLGetData(8); + string sOrReqFeat4 = PRC_SQLGetData(9); + string sReqSkill = PRC_SQLGetData(10); + string sReqSkill2 = PRC_SQLGetData(11); + string sReqSkillRanks = PRC_SQLGetData(12); + string sReqSkillRanks2 = PRC_SQLGetData(13); + + // check AND feat prerequisites + if (GetMeetsANDPreReq(sPreReqFeat1, sPreReqFeat2)) + { + // check OR feat prerequisites + if (GetMeetsORPreReq(sOrReqFeat0, sOrReqFeat1, sOrReqFeat2, sOrReqFeat3, sOrReqFeat4)) + { + // check skill prerequisites + if(GetMeetSkillPrereq(sReqSkill, sReqSkill2, sReqSkillRanks, sReqSkillRanks2)) + { + // check they don't have it already + if(!PreReqFeatArrayLoop(nRow)) + { + AddCachedChoice(sName, nRow); + } + else + { + if(DEBUG) DoDebug("Already picked feat " + IntToString(nRow) + ". Not added!"); + } + } + else + { + if(DEBUG) DoDebug("Not met skill prereq for feat " + IntToString(nRow) + ". Not added!"); + } + } + else + { + if(DEBUG) DoDebug("Not met OR prereqfeat test for feat " + IntToString(nRow) + ". Not added!"); + } + } + else + { + if(DEBUG) DoDebug("Not met AND prereqfeat test for feat " + IntToString(nRow) + ". Not added!"); + } + } // end of while(PRC_SQLFetch() == PRC_SQL_SUCCESS) + + if(nCounter == 5) + { + SetLocalInt(OBJECT_SELF, "i", nReali+5); + DelayCommand(0.01, DoFeatLoop(nClassFeatStage)); + } + else // there were less than 5 rows, it's the end of the 2da + { + if(nClassFeatStage) + { + AddChoicesFromCache(); + FloatingTextStringOnCreature("Done", OBJECT_SELF, FALSE); + if(DEBUG) DoDebug("Finished class feats"); + DeleteLocalInt(OBJECT_SELF, "DynConv_Waiting"); + DeleteLocalInt(OBJECT_SELF, "i"); + return; + } + else // run again to select class feats + { + nClassFeatStage = TRUE; + if(DEBUG) DoDebug("Finished general feats"); + DeleteLocalInt(OBJECT_SELF, "i"); + DelayCommand(0.01, DoFeatLoop(nClassFeatStage)); + } + } +} + +void DoBonusFeatLoop() +{ + /* TODO - scripting feat enforcement */ + string sSQL; + object oPC = OBJECT_SELF; + + int nFeatsRemaining = GetLocalInt(OBJECT_SELF, "Points"); + int nClass = GetLocalInt(oPC, "Class"); + + if (nClass == CLASS_TYPE_PSION && nFeatsRemaining == 2) + { + // then skip everything and just list the disciplines + /* + SELECT rowid, FEAT FROM prc_cached2da_feat + WHERE rowid >= 3554 AND rowid <= 3559 + */ + sSQL = "SELECT rowid, feat FROM prc_cached2da_feat WHERE rowid >= 3554 AND rowid <= 3559"; + PRC_SQLExecDirect(sSQL); + // to keep track of where in the 25 rows we stop getting a result + int nCounter = 0; + while(PRC_SQLFetch() == PRC_SQL_SUCCESS) + { + nCounter++; + int nRow = StringToInt(PRC_SQLGetData(1)); + int nStrRef = StringToInt(PRC_SQLGetData(2)); + string sName = GetStringByStrRef(nStrRef); + AddChoice(sName, nRow); + } + + AddChoicesFromCache(); + if(DEBUG) DoDebug("Finished bonus feats"); + FloatingTextStringOnCreature("Done", OBJECT_SELF, FALSE); + DeleteLocalInt(OBJECT_SELF, "DynConv_Waiting"); + + return; + } + + // get the information needed to work out if the prereqs are met + int nSex = GetLocalInt(oPC, "Gender"); + int nRace = GetLocalInt(oPC, "Race"); + + int nStr = GetLocalInt(oPC, "Str"); + int nDex = GetLocalInt(oPC, "Dex"); + int nCon = GetLocalInt(oPC, "Con"); + int nInt = GetLocalInt(oPC, "Int"); + int nWis = GetLocalInt(oPC, "Wis"); + int nCha = GetLocalInt(oPC, "Cha"); + int nOrder = GetLocalInt(oPC, "LawfulChaotic"); + int nMoral = GetLocalInt(oPC, "GoodEvil"); + + //add racial ability alterations + nStr += StringToInt(Get2DACache("racialtypes", "StrAdjust", nRace)); + nDex += StringToInt(Get2DACache("racialtypes", "DexAdjust", nRace)); + nCon += StringToInt(Get2DACache("racialtypes", "ConAdjust", nRace)); + nInt += StringToInt(Get2DACache("racialtypes", "IntAdjust", nRace)); + nWis += StringToInt(Get2DACache("racialtypes", "WisAdjust", nRace)); + nCha += StringToInt(Get2DACache("racialtypes", "ChaAdjust", nRace)); + + // get BAB + int nBAB = StringToInt(Get2DACache(Get2DACache("classes", "AttackBonusTable", nClass), "BAB", 0)); + + // get fortitude save + int nFortSave = StringToInt(Get2DACache(Get2DACache("classes","SavingThrowTable" , nClass), "FortSave", 0)); + + // get the results 5 rows at a time to avoid TMI + int nReali = GetLocalInt(OBJECT_SELF, "i"); + + // get which cls_feat_*** 2da to use + string sFile = GetStringLowerCase(Get2DACache("classes", "FeatsTable", nClass)); + + /* + SELECT prc_cached2da_cls_feat.FeatIndex, prc_cached2da_cls_feat.FEAT, + prc_cached2da_feat.PREREQFEAT1, prc_cached2da_feat.PREREQFEAT2, + prc_cached2da_feat.OrReqFeat0, prc_cached2da_feat.OrReqFeat1, prc_cached2da_feat.OrReqFeat2, prc_cached2da_feat.OrReqFeat3, prc_cached2da_feat.OrReqFeat4, + prc_cached2da_feat.REQSKILL, prc_cached2da_feat.REQSKILL2, + prc_cached2da_feat.ReqMinSkillRanks, prc_cached2da_feat.ReqMinSkillRanks2 + FROM prc_cached2da_cls_feat INNER JOIN prc_cached2da_feat + WHERE (prc_cached2da_feat.FEAT != '****') AND (prc_cached2da_cls_feat.FeatIndex != '****') + AND (prc_cached2da_cls_feat.file = '') + AND ((prc_cached2da_cls_feat.List = 1) OR (prc_cached2da_cls_feat.List = 2)) + AND (prc_cached2da_cls_feat.GrantedOnLevel <= 1) + AND (prc_cached2da_feat.rowid = prc_cached2da_cls_feat.FeatIndex) + AND (`PreReqEpic` != 1) + AND (`MinLevel` = '****' OR `MinLevel` = '1') + AND `ALLCLASSESCANUSE` = 0 + AND `minattackbonus` <= + AND `minspelllvl` <= 1 + AND `minstr`<= + AND `mindex`<= + AND `mincon`<= + AND `minint`<= + AND `minwis`<= + AND `mincha`<= + AND `MinFortSave` <= + */ + + sSQL = "SELECT prc_cached2da_feat.rowid, feat, prereqfeat1, prereqfeat2, " + +"orreqfeat0, orreqfeat1, orreqfeat2, orreqfeat3, orreqfeat4, " + +"reqskill, reqskill2, reqskillminranks, reqskillminranks2" + +" FROM prc_cached2da_feat INNER JOIN prc_cached2da_cls_feat" + +" WHERE (prc_cached2da_feat.feat != '****') AND (prc_cached2da_cls_feat.featindex != '****')" + +" AND (prc_cached2da_cls_feat.file = '" + sFile + "')" + +" AND ((prc_cached2da_cls_feat.list = 1) OR (prc_cached2da_cls_feat.list = 2))" + +" AND (prc_cached2da_cls_feat.grantedonlevel <= 1)" + +" AND (prc_cached2da_feat.rowid = prc_cached2da_cls_feat.featindex)" + +" AND (prereqepic != 1)" + +" AND (minlevel = '****' OR minlevel = '1')" + +" AND (minattackbonus <= "+IntToString(nBAB)+")" + +" AND (minspelllvl <= 1)" + +" AND (minstr <= "+IntToString(nStr)+")" + +" AND (mindex <= "+IntToString(nDex)+")" + +" AND (mincon <= "+IntToString(nCon)+")" + +" AND (minint <= "+IntToString(nInt)+")" + +" AND (minwis <= "+IntToString(nWis)+")" + +" AND (mincha <= "+IntToString(nCha)+")" + +" AND (minfortsave <= "+IntToString(nFortSave)+")" + +" LIMIT 5 OFFSET "+IntToString(nReali); + + // debug print the sql statement + if(DEBUG) + { + DoDebug(sSQL); + } + + PRC_SQLExecDirect(sSQL); + // to keep track of where in the 25 rows we stop getting a result + int nCounter = 0; + while(PRC_SQLFetch() == PRC_SQL_SUCCESS) + { + nCounter++; + int nRow = StringToInt(PRC_SQLGetData(1)); + int nStrRef = StringToInt(PRC_SQLGetData(2)); + string sName = GetStringByStrRef(nStrRef); + string sPreReqFeat1 = PRC_SQLGetData(3); + string sPreReqFeat2 = PRC_SQLGetData(4); + string sOrReqFeat0 = PRC_SQLGetData(5); + string sOrReqFeat1 = PRC_SQLGetData(6); + string sOrReqFeat2 = PRC_SQLGetData(7); + string sOrReqFeat3 = PRC_SQLGetData(8); + string sOrReqFeat4 = PRC_SQLGetData(9); + string sReqSkill = PRC_SQLGetData(10); + string sReqSkill2 = PRC_SQLGetData(11); + string sReqSkillRanks = PRC_SQLGetData(12); + string sReqSkillRanks2 = PRC_SQLGetData(13); + + // check AND feat prerequisites + if (GetMeetsANDPreReq(sPreReqFeat1, sPreReqFeat2)) + { + // check OR feat prerequisites + if (GetMeetsORPreReq(sOrReqFeat0, sOrReqFeat1, sOrReqFeat2, sOrReqFeat3, sOrReqFeat4)) + { + // check skill prerequisites + if(GetMeetSkillPrereq(sReqSkill, sReqSkill2, sReqSkillRanks, sReqSkillRanks2)) + { + // check they don't have it already + if(!PreReqFeatArrayLoop(nRow)) + { + // check it's not a psion discipline + if (nClass != CLASS_TYPE_PSION || !(nRow >= 3554 && nRow <= 3559)) + AddChoice(sName, nRow); + } + else + { + if(DEBUG) DoDebug("Already picked feat " + IntToString(nRow) + ". Not added!"); + } + } + else + { + if(DEBUG) DoDebug("Not met skill prereq for feat " + IntToString(nRow) + ". Not added!"); + } + } + else + { + if(DEBUG) DoDebug("Not met OR prereqfeat test for feat " + IntToString(nRow) + ". Not added!"); + } + } + else + { + if(DEBUG) DoDebug("Not met AND prereqfeat test for feat " + IntToString(nRow) + ". Not added!"); + } + } // end of while(PRC_SQLFetch() == PRC_SQL_SUCCESS) + + if(nCounter == 5) + { + SetLocalInt(OBJECT_SELF, "i", nReali+5); + DelayCommand(0.01, DoBonusFeatLoop()); + } + else // there were less than 5 rows, it's the end of the 2da + { + AddChoicesFromCache(); + if(DEBUG) DoDebug("Finished bonus feats"); + FloatingTextStringOnCreature("Done", OBJECT_SELF, FALSE); + DeleteLocalInt(OBJECT_SELF, "DynConv_Waiting"); + DeleteLocalInt(OBJECT_SELF, "i"); + return; + } +} + +void DoSpellsLoop(int nStage) +{ + // get which spell level the choices are for + int nSpellLevel = 0; + if (nStage == STAGE_SPELLS_1) + nSpellLevel = 1; + int nClass = GetLocalInt(OBJECT_SELF, "Class"); + string sSQL; + // get the results 30 rows at a time to avoid TMI + int nReali = GetLocalInt(OBJECT_SELF, "i"); + switch(nClass) + { + case CLASS_TYPE_WIZARD: { + int nSpellSchool = GetLocalInt(OBJECT_SELF, "School"); + string sOpposition = Get2DACache("spellschools", "letter", StringToInt(Get2DACache("spellschools", "opposition", nSpellSchool))); + sSQL = "SELECT rowid, name FROM prc_cached2da_spells WHERE (name != '****') AND (wiz_sorc = '1') AND (school != '"+sOpposition+"') LIMIT 30 OFFSET "+IntToString(nReali); + break; + } + case CLASS_TYPE_SORCERER: { + sSQL = "SELECT rowid, name FROM prc_cached2da_spells WHERE (name != '****') AND(wiz_sorc = '"+IntToString(nSpellLevel)+"') LIMIT 30 OFFSET "+IntToString(nReali); + break; + } + case CLASS_TYPE_BARD: { + sSQL = "SELECT rowid, name FROM prc_cached2da_spells WHERE (name != '****') AND(bard = '"+IntToString(nSpellLevel)+"') LIMIT 30 OFFSET "+IntToString(nReali); + break; + } + } + + PRC_SQLExecDirect(sSQL); + // to keep track of where in the 10 rows we stop getting a result + int nCounter = 0; + while(PRC_SQLFetch() == PRC_SQL_SUCCESS) + { + nCounter++; + // has it already been chosen? + int nSpell = StringToInt(PRC_SQLGetData(1)); + // if they don't know the spell, add it to the choice list + if(!GetIsSpellKnown(nSpell, nSpellLevel)) + { + string sName = GetStringByStrRef(StringToInt(PRC_SQLGetData(2))); + AddChoice(sName, nSpell); + } + } // end of while(PRC_SQLFetch() == PRC_SQL_SUCCESS) + + if (nCounter == 30) + { + SetLocalInt(OBJECT_SELF, "i", nReali+30); + DelayCommand(0.01, DoSpellsLoop(nStage)); + } + else // end of the 2da + { + if(DEBUG) DoDebug("Finished spells"); + FloatingTextStringOnCreature("Done", OBJECT_SELF, FALSE); + DeleteLocalInt(OBJECT_SELF, "i"); + DeleteLocalInt(OBJECT_SELF, "DynConv_Waiting"); + return; + } +} + +void DoDomainsLoop() +{ + int i = 0; + string sName; + // get the first domain chosen if it's there + int nDomain = GetLocalInt(OBJECT_SELF, "Domain1"); + + // genasi elemental domain enforcement only is needed on the first domain + if (!nDomain && GetPRCSwitch(PRC_CONVOCC_GENASI_ENFORCE_DOMAINS)) + { + // now check PC race + int nRace = GetLocalInt(OBJECT_SELF, "Race"); + if(nRace == RACIAL_TYPE_AIR_GEN) + i = DOMAIN_AIR; + else if(nRace == RACIAL_TYPE_EARTH_GEN) + i = DOMAIN_EARTH; + else if(nRace == RACIAL_TYPE_FIRE_GEN) + i = DOMAIN_FIRE; + else if(nRace == RACIAL_TYPE_WATER_GEN) + i = DOMAIN_WATER; + } + // see if i was just set + if (i) // if set, then the player gets no choice + { + i--; // the domain constants are offset by 1 to their 2da lines + sName = Get2DACache("domains", "Name", i); + AddChoice(GetStringByStrRef(StringToInt(sName)), i); + } + else // give them the full domain list + { + // get the file end + int nFileEnd = GetPRCSwitch(FILE_END_DOMAINS); + /* SELECT statement + * SELECT rowid, data FROM prc_cached2da + * WHERE file = 'domains' AND columnid = 'name' AND data != '****' AND rowid >= + */ + string sSQL = "SELECT rowid, data FROM prc_cached2da WHERE file = 'domains' AND columnid = 'name' AND data != '****' AND rowid <= " + IntToString(nFileEnd); + PRC_SQLExecDirect(sSQL); + + // fix for air domain being 0 + // swap around -1 (actually air/0) and variable not set (0) + if (nDomain == -1) // air domain + nDomain = 0; + else if (nDomain == 0) // not set + nDomain = -1; + int i; + string sRowID; + string sName; + while(PRC_SQLFetch() == PRC_SQL_SUCCESS) + { + i = StringToInt(PRC_SQLGetData(1)); // rowid + sName = GetStringByStrRef(StringToInt(PRC_SQLGetData(2))); // data + if (i != nDomain) + { + AddChoice(sName, i); + } + } + } + DeleteLocalInt(OBJECT_SELF, "DynConv_Waiting"); + FloatingTextStringOnCreature("Done", OBJECT_SELF, FALSE); +} + +void DoAppearanceLoop() +{ + // get the results 100 rows at a time to avoid TMI + int nReali = GetLocalInt(OBJECT_SELF, "i"); + int nCounter = 0; + string sSQL = "SELECT rowid, string_ref, label FROM prc_cached2da_appearance WHERE (label != '****') LIMIT 100 OFFSET "+IntToString(nReali); + PRC_SQLExecDirect(sSQL); + while(PRC_SQLFetch() == PRC_SQL_SUCCESS) + { + nCounter++; + string sName; + int nStrRef = StringToInt(PRC_SQLGetData(2)); + if(nStrRef) + sName = GetStringByStrRef(nStrRef); + else + sName = PRC_SQLGetData(3); + int nRow = StringToInt(PRC_SQLGetData(1)); + AddChoice(sName, nRow); + } + if (nCounter == 100) + { + SetLocalInt(OBJECT_SELF, "i", nReali+100); + DelayCommand(0.01, DoAppearanceLoop()); + } + else // end of the 2da + { + if(DEBUG) DoDebug("Finished appearances"); + FloatingTextStringOnCreature("Done", OBJECT_SELF, FALSE); + DeleteLocalInt(OBJECT_SELF, "i"); + DeleteLocalInt(OBJECT_SELF, "DynConv_Waiting"); + return; + } +} + +void DoPortraitsLoop(int nGenderSort = TRUE) +{ + // get the results 100 rows at a time to avoid TMI + int nReali = GetLocalInt(OBJECT_SELF, "i"); + int nCounter = 0; + string sSQL; + // get the gender and add it to the SQL statement + string sGender = IntToString(GetLocalInt(OBJECT_SELF, "Gender")); + // get the race for ordering + string sRace; + // map the race to appearance for the PC appearances + // as eg. all drow portraits are labelled as elf portraits in bioware's portraits.2da + int nFakeRace = MapAppearanceToRace(GetLocalInt(OBJECT_SELF, "Appearance")); + if (nFakeRace != -1) // if the appearance was a default character model + sRace = IntToString(nFakeRace); + else // use the actual race + sRace = IntToString(GetLocalInt(OBJECT_SELF, "Race")); + + // note: "BaseResRef != 'matron'" is because that portrait is referenced in bioware's + // portraits.2da, but doesn't exist + if (nGenderSort) + { + if(GetPRCSwitch(PRC_CONVOCC_USE_RACIAL_PORTRAIT)) // get only race specific ones + { + sSQL = "SELECT rowid, baseresref, FROM prc_cached2da_portraits WHERE (inanimatetype = '****') AND (baseresref != '****') AND (baseresref != 'matron') AND (sex = '" + sGender + + "') AND (race ='"+sRace+"') LIMIT 100 OFFSET "+IntToString(nReali); + } + else + { + // orders portraits of PC's race first, only PC's gender-specific portraits + sSQL = "SELECT rowid, baseresref, CASE race WHEN "+ sRace +" THEN 0 else 1 END AS column1 FROM prc_cached2da_portraits WHERE (inanimatetype = '****') AND (baseresref != '****') AND (baseresref != 'matron') AND (sex = '" + sGender + + "') ORDER BY column1, race LIMIT 100 OFFSET "+IntToString(nReali); + } + } + else + { + if(GetPRCSwitch(PRC_CONVOCC_USE_RACIAL_PORTRAIT)) // get only race specific ones + { + sSQL = "SELECT rowid, baseresref, FROM prc_cached2da_portraits WHERE (inanimatetype = '****') AND (baseresref != '****') AND (baseresref != 'matron') AND (sex != '" + sGender + + "') AND (race ='"+sRace+"') LIMIT 100 OFFSET "+IntToString(nReali); + } + else + { + sSQL = "SELECT rowid, baseresref FROM prc_cached2da_portraits WHERE (inanimatetype = '****') AND (baseresref != '****') AND (baseresref != 'matron') AND (sex != '" + sGender + "') LIMIT 100 OFFSET "+IntToString(nReali); + } + } + PRC_SQLExecDirect(sSQL); + while(PRC_SQLFetch() == PRC_SQL_SUCCESS) + { + nCounter++; + int nRow = StringToInt(PRC_SQLGetData(1)); + string sName = PRC_SQLGetData(2); + AddChoice(sName, nRow); + } + if (nCounter == 100) + { + SetLocalInt(OBJECT_SELF, "i", nReali+100); + DelayCommand(0.01, DoPortraitsLoop(nGenderSort)); + } + else // end of the 2da + { + if (nGenderSort && !GetPRCSwitch(PRC_CONVOCC_RESTRICT_PORTRAIT_BY_SEX)) // run again to select the rest of the portraits + { + nGenderSort = FALSE; + if(DEBUG) DoDebug("Finished gender specific portraits"); + DeleteLocalInt(OBJECT_SELF, "i"); + DelayCommand(0.01, DoPortraitsLoop(nGenderSort)); + } + else // done + { + if(DEBUG) DoDebug("Finished portraits"); + FloatingTextStringOnCreature("Done", OBJECT_SELF, FALSE); + DeleteLocalInt(OBJECT_SELF, "i"); + DeleteLocalInt(OBJECT_SELF, "DynConv_Waiting"); + return; + } + } +} + +void DoSoundsetLoop(int nGenderSort = TRUE) +{ + // get the results 100 rows at a time to avoid TMI + int nReali = GetLocalInt(OBJECT_SELF, "i"); + int nCounter = 0; + string sSQL; + string sGender = IntToString(GetLocalInt(OBJECT_SELF, "Gender")); + + sSQL = "SELECT rowid, strref FROM prc_cached2da_soundset WHERE (resref != '****')"; + + if(nGenderSort) + { + sSQL += " AND (gender = '" + sGender + "')"; + } + else + { + sSQL += " AND (gender != '" + sGender + "')"; + } + + // make the SQL string + if(GetPRCSwitch(PRC_CONVOCC_ONLY_PLAYER_VOICESETS)) + sSQL += " AND (TYPE = '0') LIMIT 100 OFFSET "+IntToString(nReali); + else + sSQL += " ORDER BY TYPE LIMIT 100 OFFSET "+IntToString(nReali); + + PRC_SQLExecDirect(sSQL); + while(PRC_SQLFetch() == PRC_SQL_SUCCESS) + { + nCounter++; + int nRow = StringToInt(PRC_SQLGetData(1)); + string sName = GetStringByStrRef(StringToInt(PRC_SQLGetData(2))); + AddChoice(sName, nRow); + } + + if (nCounter == 100) + { + SetLocalInt(OBJECT_SELF, "i", nReali+100); + DelayCommand(0.01, DoSoundsetLoop()); + } + else // end of the 2da + { + if (nGenderSort && !GetPRCSwitch(PRC_CONVOCC_RESTRICT_VOICESETS_BY_SEX)) // run again to select the rest of the voicesets + { + nGenderSort = FALSE; + if(DEBUG) DoDebug("Finished gender specific voicesets"); + DeleteLocalInt(OBJECT_SELF, "i"); + DelayCommand(0.01, DoPortraitsLoop(nGenderSort)); + } + else // done + { + if(DEBUG) DoDebug("Finished Soundsets"); + FloatingTextStringOnCreature("Done", OBJECT_SELF, FALSE); + DeleteLocalInt(OBJECT_SELF, "i"); + DeleteLocalInt(OBJECT_SELF, "DynConv_Waiting"); + return; + } + } +} + +void DoWingmodelLoop() +{ + if (GetPRCSwitch(PRC_CONVOCC_AVARIEL_WINGS) && GetLocalInt(OBJECT_SELF, "Race") == RACIAL_TYPE_AVARIEL) + AddChoice("Bird", 6); + else if (GetPRCSwitch(PRC_CONVOCC_FEYRI_WINGS) && GetLocalInt(OBJECT_SELF, "Race") == RACIAL_TYPE_FEYRI) + AddChoice("Bat", 3); + else if (GetPRCSwitch(PRC_CONVOCC_AASIMAR_WINGS) && GetLocalInt(OBJECT_SELF, "Race") == RACIAL_TYPE_AASIMAR) + { + AddChoice("None", 0); + AddChoice("Angel", 2); + } + else if (GetPRCSwitch(PRC_CONVOCC_DISALLOW_CUSTOMISE_WINGS)) + AddChoice("None", 0); + else + { + string sSQL; + + sSQL = "SELECT rowid, data FROM prc_cached2da WHERE file='wingmodel' AND columnid = 'label' AND data != '****'"; + + PRC_SQLExecDirect(sSQL); + while(PRC_SQLFetch() == PRC_SQL_SUCCESS) + { + int nRow = StringToInt(PRC_SQLGetData(1)); + string sName = PRC_SQLGetData(2); + AddChoice(sName, nRow); + } + } +} + +void DoTailmodelLoop() +{ + if (GetPRCSwitch(PRC_CONVOCC_FEYRI_TAIL) && GetLocalInt(OBJECT_SELF, "Race") == RACIAL_TYPE_FEYRI) + AddChoice("Devil", 3); + else if (GetPRCSwitch(PRC_CONVOCC_TIEFLING_TAIL) && GetLocalInt(OBJECT_SELF, "Race") == RACIAL_TYPE_TIEFLING) + { + AddChoice("None", 0); + AddChoice("Devil", 3); + } + else if (GetPRCSwitch(PRC_CONVOCC_DISALLOW_CUSTOMISE_TAIL)) + AddChoice("None", 0); + else + { + string sSQL; + + sSQL = "SELECT rowid, data FROM prc_cached2da WHERE file='tailmodel' AND columnid = 'label' AND data != '****'"; + + PRC_SQLExecDirect(sSQL); + while(PRC_SQLFetch() == PRC_SQL_SUCCESS) + { + int nRow = StringToInt(PRC_SQLGetData(1)); + string sName = PRC_SQLGetData(2); + AddChoice(sName, nRow); + } + } +} + +void AddRaceFeats(int nRace) +{ + // gets which race_feat***.2da to use + string sFile = GetStringLowerCase(Get2DACache("racialtypes", "FeatsTable", nRace)); + // create the Feats array + array_create(OBJECT_SELF, "Feats"); + int nQTMCount = 0; + // add on any bonus feats from switches here + nQTMCount += (GetPRCSwitch(PRC_CONVOCC_BONUS_FEATS)); + /* + SELECT `data` FROM `prc_cached2da` + WHERE (`file` = ) AND (`columnid` = 'FeatIndex') AND (`data` != '****') + */ + string sSQL = "SELECT data FROM prc_cached2da WHERE (file = '" + sFile + "') AND (columnid = 'featindex') AND (data != '****')"; + PRC_SQLExecDirect(sSQL); + int nFeat; + while(PRC_SQLFetch() == PRC_SQL_SUCCESS) + { + nFeat = StringToInt(PRC_SQLGetData(1)); // feat index + //alertness fix + if(nFeat == 0) + nFeat = -1; + // if one of these is quick to master, mark for doing the bonus feat later + if (nFeat == 258) + nQTMCount++; + array_set_int(OBJECT_SELF, "Feats", array_get_size(OBJECT_SELF, "Feats"), nFeat); + } + // set final bonus feat count + SetLocalInt(OBJECT_SELF, "QTM", nQTMCount); +} + +void AddClassFeats(int nClass) +{ + // gets which class_feat_***.2da to use + string sFile = GetStringLowerCase(Get2DACache("classes", "FeatsTable", nClass)); + // Feats array should already exist, but check anyway + int nArraySize = array_get_size(OBJECT_SELF, "Feats"); + if (nArraySize) // if there's stuff in there already + { + // has it's own table, so SQL is easiest + // class feats granted at level 1 + /* + SELECT `FeatIndex` FROM `prc_cached2da_cls_feat` + WHERE (`file` = ) AND (`List` = 3) AND (`GrantedOnLevel` = 1) AND (`FeatIndex` != '****') + */ + string sSQL = "SELECT featindex FROM prc_cached2da_cls_feat WHERE (file = '" + sFile + "') AND (list = 3) AND (grantedonlevel = 1) AND (featindex != '****')"; + PRC_SQLExecDirect(sSQL); + int nFeat; + while (PRC_SQLFetch() == PRC_SQL_SUCCESS) + { + nFeat = StringToInt(PRC_SQLGetData(1)); // feat index + //alertness fix + if(nFeat == 0) + nFeat = -1; + array_set_int(OBJECT_SELF, "Feats", array_get_size(OBJECT_SELF, "Feats"), nFeat); + } + } + else // no feat array - screw up + { + /* TODO - start again */ + } + +} + +void AddDomainFeats() +{ + int nDomain = GetLocalInt(OBJECT_SELF, "Domain1"); + // air domain fix + if (nDomain == -1) + nDomain = 0; + // get feat + string sFeat = Get2DACache("domains", "GrantedFeat", nDomain); + // add to the feat array + array_set_int(OBJECT_SELF, "Feats", array_get_size(OBJECT_SELF, "Feats"), + StringToInt(sFeat)); + + nDomain = GetLocalInt(OBJECT_SELF, "Domain2"); + // air domain fix + if (nDomain == -1) + nDomain = 0; + sFeat = Get2DACache("domains", "GrantedFeat", nDomain); + // add to the feat array + array_set_int(OBJECT_SELF, "Feats", array_get_size(OBJECT_SELF, "Feats"), + StringToInt(sFeat)); +} + +void AddColourChoices(int nStage, int nCategory) +{ + // get which 2da column to use + string s2DAColumn; + if (nStage == STAGE_SKIN_COLOUR_CHOICE) + s2DAColumn = "skin"; + else if (nStage == STAGE_HAIR_COLOUR_CHOICE) + s2DAColumn = "hair"; + else // it's one of the tattoo colour stages + s2DAColumn = "cloth"; + + // get which rows to loop through + int nStart = 0; + int nStop = 0; + switch(nCategory) // the category determines which colours get listed + { + case 1: + nStart = 0; + nStop = 7; + break; + case 2: + nStart = 8; + nStop = 15; + break; + case 3: + nStart = 16; + nStop = 23; + break; + case 4: + nStart = 24; + nStop = 31; + break; + case 5: + nStart = 32; + nStop = 39; + break; + case 6: + nStart = 40; + nStop = 47; + break; + case 7: + nStart = 48; + nStop = 55; + break; + case 8: // new colours + nStart = 56; + nStop = 63; + break; + case 9: + nStart = 64; + nStop = 71; + break; + case 10: + nStart = 72; + nStop = 79; + break; + case 11: + nStart = 80; + nStop = 87; + break; + case 12: + nStart = 88; + nStop = 95; + break; + case 13: + nStart = 96; + nStop = 103; + break; + case 14: + nStart = 104; + nStop = 111; + break; + case 15: + nStart = 112; + nStop = 119; + break; + case 16: + nStart = 120; + nStop = 127; + break; + case 17: + nStart = 128; + nStop = 135; + break; + case 18: + nStart = 136; + nStop = 143; + break; + case 19: + nStart = 144; + nStop = 151; + break; + case 20: + nStart = 152; + nStop = 159; + break; + case 21: + nStart = 160; + nStop = 167; + break; + case 22: + nStart = 168; + nStop = 175; + } + // make the list + int i = nStart; + while (i <= nStop) + { + AddChoice(Get2DACache("colours", s2DAColumn, i), i); + i++; + } +} +