/** * 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++; } }