2373 lines
		
	
	
		
			85 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			2373 lines
		
	
	
		
			85 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| /**
 | |
|  * 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 "<statvalue> (racial <+/-modifier>) <statname>. Cost to increase <cost>" 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<GetStringLength(sPublicCDKey);i++)
 | |
|     {
 | |
|         nKeyTotal += StringToInt(GetStringLeft(GetStringRight(sPublicCDKey, i),1));
 | |
|     }
 | |
|     sReturn = IntToString(nKeyTotal);
 | |
|     return sReturn;
 | |
| }
 | |
| 
 | |
| void DoStripPC(object oPC)
 | |
| {
 | |
|     // remove equipped items
 | |
|     int i;
 | |
|     for(i=0; i<NUM_INVENTORY_SLOTS; i++)
 | |
|     {
 | |
|         object oEquip = GetItemInSlot(i,oPC);
 | |
|         if(GetIsObjectValid(oEquip))
 | |
|         {
 | |
|             SetPlotFlag(oEquip,FALSE);
 | |
|             DestroyObject(oEquip);
 | |
|         }
 | |
|     }
 | |
|     // empty inventory
 | |
|     object oItem = GetFirstItemInInventory(oPC);
 | |
|     while(GetIsObjectValid(oItem))
 | |
|     {
 | |
|         SetPlotFlag(oItem,FALSE);
 | |
|         DestroyObject(oItem);
 | |
|         oItem = GetNextItemInInventory(oPC);
 | |
|     }
 | |
| }
 | |
| 
 | |
| void CheckAndBoot(object oPC)
 | |
| {
 | |
|     if(GetIsObjectValid(GetAreaFromLocation(GetLocation(oPC))))
 | |
|         BootPC(oPC);
 | |
| }
 | |
| 
 | |
| void CheckAndBootNicely(object oPC)
 | |
| {
 | |
|     // Give a "helpful" message
 | |
|     string sMessage = GetLocalString(oPC, "CONVOCC_ENTER_BOOT_MESSAGE");
 | |
|     if (sMessage == "") // yes, you should have given your own message here
 | |
|         sMessage = "Eeek! Something's not right.";
 | |
|     // no avoiding the convoCC, so stop them running off
 | |
|     effect eParal = EffectCutsceneImmobilize();
 | |
|     eParal = SupernaturalEffect(eParal);
 | |
|     ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eParal, oPC, 9999.9);
 | |
|     // floaty text info as we can't use the dynamic convo for this
 | |
|     DelayCommand(10.0, FloatingTextStringOnCreature(sMessage, oPC, FALSE));
 | |
|     DelayCommand(11.0, FloatingTextStringOnCreature("You will be booted in 5...", oPC, FALSE));
 | |
|     DelayCommand(12.0, FloatingTextStringOnCreature("4...", oPC, FALSE));
 | |
|     DelayCommand(13.0, FloatingTextStringOnCreature("3...", oPC, FALSE));
 | |
|     DelayCommand(14.0, FloatingTextStringOnCreature("2...", oPC, FALSE));
 | |
|     DelayCommand(15.0, FloatingTextStringOnCreature("1...", oPC, FALSE));
 | |
|     AssignCommand(oPC, DelayCommand(16.0, CheckAndBoot(oPC)));
 | |
| }
 | |
| 
 | |
| void DoSetRaceAppearance(object oPC)
 | |
| {
 | |
|     if(DEBUG) DoDebug(DebugObject2Str(oPC));
 | |
|     // sets the appearance type
 | |
|     int nSex = GetLocalInt(oPC, "Gender");
 | |
|     int nRace = GetLocalInt(oPC, "Race");
 | |
|     // appearance type switches go here
 | |
|     if(nRace == RACIAL_TYPE_RAKSHASA || nRace == RACIAL_TYPE_ZAKYA_RAKSHASA || nRace == RACIAL_TYPE_NAZTHARUNE_RAKSHASA
 | |
|         && nSex == GENDER_FEMALE
 | |
|         && GetPRCSwitch(PRC_CONVOCC_RAKSHASA_FEMALE_APPEARANCE))
 | |
|         SetCreatureAppearanceType(oPC, APPEARANCE_TYPE_RAKSHASA_TIGER_FEMALE);
 | |
|     else
 | |
|         SetCreatureAppearanceType(oPC, 
 | |
|                     StringToInt(Get2DACache("racialtypes", "Appearance",
 | |
|                         GetLocalInt(oPC, "Race"))));
 | |
|     
 | |
|     // remove wings and tails - so this can be set later
 | |
|     // enforcing wing and tail related switches comes later too
 | |
|     SetCreatureWingType(CREATURE_WING_TYPE_NONE);
 | |
|     SetCreatureTailType(CREATURE_TAIL_TYPE_NONE);
 | |
|     
 | |
|     // get rid of invisible/undead etc bits, but keep tatoos as is
 | |
|     SetCreatureBodyPart(CREATURE_PART_RIGHT_FOOT, 1);
 | |
|     SetCreatureBodyPart(CREATURE_PART_LEFT_FOOT, 1);
 | |
|     SetCreatureBodyPart(CREATURE_PART_PELVIS, 1);
 | |
|     SetCreatureBodyPart(CREATURE_PART_BELT, 0);
 | |
|     SetCreatureBodyPart(CREATURE_PART_NECK, 1);
 | |
|     SetCreatureBodyPart(CREATURE_PART_RIGHT_SHOULDER, 0);
 | |
|     SetCreatureBodyPart(CREATURE_PART_LEFT_SHOULDER, 0);
 | |
|     SetCreatureBodyPart(CREATURE_PART_RIGHT_HAND, 1);
 | |
|     SetCreatureBodyPart(CREATURE_PART_LEFT_HAND, 1);
 | |
|     // make invisible heads visible, otherwise leave
 | |
|     if(GetCreatureBodyPart(CREATURE_PART_HEAD) == 0)
 | |
|         SetCreatureBodyPart(CREATURE_PART_HEAD, 1);
 | |
|     // preserve tattoos, get rid of anything else
 | |
|     if(!(GetCreatureBodyPart(CREATURE_PART_RIGHT_SHIN) == 1))
 | |
|         SetCreatureBodyPart(CREATURE_PART_RIGHT_SHIN, 2);
 | |
|     if(!(GetCreatureBodyPart(CREATURE_PART_LEFT_SHIN) == 1))
 | |
|         SetCreatureBodyPart(CREATURE_PART_LEFT_SHIN, 2);
 | |
|     if(!(GetCreatureBodyPart(CREATURE_PART_RIGHT_THIGH) == 1))
 | |
|         SetCreatureBodyPart(CREATURE_PART_RIGHT_THIGH, 2);
 | |
|     if(!(GetCreatureBodyPart(CREATURE_PART_LEFT_THIGH) == 1))
 | |
|         SetCreatureBodyPart(CREATURE_PART_LEFT_THIGH, 2);
 | |
|     if(!(GetCreatureBodyPart(CREATURE_PART_RIGHT_FOREARM) == 1))
 | |
|         SetCreatureBodyPart(CREATURE_PART_RIGHT_FOREARM, 2);
 | |
|     if(!(GetCreatureBodyPart(CREATURE_PART_LEFT_FOREARM) == 1))
 | |
|         SetCreatureBodyPart(CREATURE_PART_LEFT_FOREARM, 2);
 | |
|     if(!(GetCreatureBodyPart(CREATURE_PART_RIGHT_BICEP) == 1))
 | |
|         SetCreatureBodyPart(CREATURE_PART_RIGHT_BICEP, 2);
 | |
|     if(!(GetCreatureBodyPart(CREATURE_PART_RIGHT_BICEP) == 1))
 | |
|         SetCreatureBodyPart(CREATURE_PART_RIGHT_BICEP, 2);
 | |
| }
 | |
| 
 | |
| void DoCloneGender(object oPC)
 | |
| {
 | |
|     object oClone = GetLocalObject(oPC, "Clone");
 | |
|     if(!GetIsObjectValid(oClone))
 | |
|         return;
 | |
|     int nSex = GetLocalInt(oPC, "Gender");
 | |
|     int nCurrentSex = GetGender(oClone);
 | |
|     StackedLetoScript(LetoSet("Gender", IntToString(nSex), "byte"));
 | |
|     // if the gender needs changing, reset the soundset
 | |
|     if (nSex != nCurrentSex)
 | |
|         StackedLetoScript(LetoSet("SoundSetFile", IntToString(0), "word"));
 | |
|     string sResult;
 | |
|     RunStackedLetoScriptOnObject(oClone, "OBJECT", "SPAWN", "prc_ccc_app_lspw", TRUE);
 | |
|     sResult = GetLocalString(GetModule(), "LetoResult");
 | |
|     SetLocalObject(GetModule(), "PCForThread"+sResult, oPC);
 | |
| }
 | |
| 
 | |
| void DoSetAppearance(object oPC)
 | |
| {
 | |
|     if(DEBUG) DoDebug(DebugObject2Str(oPC));
 | |
|     // get the appearance type
 | |
|     int nAppearance = GetLocalInt(oPC, "Appearance");
 | |
|     // get the clone object
 | |
|     object oClone = GetLocalObject(oPC, "Clone");
 | |
|     SetCreatureAppearanceType(oClone, nAppearance);
 | |
| }
 | |
| 
 | |
| void DoCutscene(object oPC, int nSetup = FALSE)
 | |
| {
 | |
|     string sScript;
 | |
|     int nStage = GetStage(oPC);
 | |
|     
 | |
|     // check the PC has finished entering the area
 | |
|     if(!GetIsObjectValid(GetArea(oPC)))
 | |
|     {
 | |
|         DelayCommand(1.0, DoCutscene(oPC, nSetup));
 | |
|         return;
 | |
|     }
 | |
|     
 | |
|     if(DEBUG) DoDebug("DoCutscene() stage is :" + IntToString(nStage) + " nSetup = " + IntToString(nSetup));
 | |
|     
 | |
|     if (nStage >= STAGE_RACE_CHECK) // if we don't need to set the clone up
 | |
|     {
 | |
|         object oClone;
 | |
|         
 | |
|         if(nStage == STAGE_RACE_CHECK || (nStage > STAGE_RACE_CHECK && nSetup))
 | |
|         {
 | |
|             // make the PC look like the race they have chosen
 | |
|             DoSetRaceAppearance(oPC);
 | |
|             // clone the PC and hide the swap with a special effect
 | |
|             // make the real PC non-collideable
 | |
|             effect eGhost = EffectCutsceneGhost();
 | |
|             ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eGhost, oPC, 99999999.9);
 | |
|             // make the swap and hide with an effect
 | |
|             effect eVis = EffectVisualEffect(VFX_FNF_SUMMON_MONSTER_1);
 | |
|             ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eVis, GetLocation(oPC));
 | |
|             // make clone
 | |
|             oClone = CopyObject(oPC, GetLocation(oPC), OBJECT_INVALID, "PlayerClone");
 | |
|             ChangeToStandardFaction(oClone, STANDARD_FACTION_MERCHANT);
 | |
|             // make the real PC invisible
 | |
|             effect eInvis = EffectVisualEffect(VFX_DUR_CUTSCENE_INVISIBILITY);
 | |
|             ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eInvis, oPC, 9999.9);
 | |
|             // swap local objects
 | |
|             SetLocalObject(oPC, "Clone", oClone);
 | |
|             SetLocalObject(oClone, "Master", oPC);
 | |
|             // this makes sure the clone gets destroyed if the PC leaves the game
 | |
|             AssignCommand(oClone, CloneMasterCheck());
 | |
|             // end of clone making
 | |
|             
 | |
|             int nGender = GetLocalInt(oPC, "Gender");
 | |
|             // this only needs doing if the gender has changed
 | |
|             if (GetGender(oPC) != nGender)
 | |
|             {
 | |
|                 sScript = LetoSet("Gender", IntToString(nGender), "byte");
 | |
|                 // reset soundset only if we've not changed it yet
 | |
|                 if (nStage < STAGE_SOUNDSET)
 | |
|                     sScript += LetoSet("SoundSetFile", IntToString(0), "word");
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         if(nStage == STAGE_APPEARANCE || (nStage > 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 = "<race>" AND (gender.data = "<gender>" 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 = <s2DA> AND columnid = <sColumnName> AND rowid >= <nFileEnd>
 | |
|      */
 | |
|      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 <nReali>
 | |
|     */
 | |
|     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 <nReali>
 | |
|     */
 | |
|     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` <= <nBAB>
 | |
|         AND `minspelllvl` <= 1
 | |
|         AND `minstr`<= <nStr>
 | |
|         AND `mindex`<= <nDex>
 | |
|         AND `mincon`<= <nCon>
 | |
|         AND `minint`<= <nInt>
 | |
|         AND `minwis`<= <nWis>
 | |
|         AND `mincha`<= <nCha>
 | |
|         AND `MinFortSave` <= <nFortSave>
 | |
|         */
 | |
| 
 | |
|         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 = '<cls_feat***>')
 | |
|             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` <= <nBAB>
 | |
|             AND `minspelllvl` <= 1
 | |
|             AND `minstr`<= <nStr>
 | |
|             AND `mindex`<= <nDex>
 | |
|             AND `mincon`<= <nCon>
 | |
|             AND `minint`<= <nInt>
 | |
|             AND `minwis`<= <nWis>
 | |
|             AND `mincha`<= <nCha>
 | |
|             AND `MinFortSave` <= <nFortSave>
 | |
|         */
 | |
| 
 | |
|         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 = '<cls_feat***>')
 | |
|             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` <= <nBAB>
 | |
|             AND `minspelllvl` <= 1
 | |
|             AND `minstr`<= <nStr>
 | |
|             AND `mindex`<= <nDex>
 | |
|             AND `mincon`<= <nCon>
 | |
|             AND `minint`<= <nInt>
 | |
|             AND `minwis`<= <nWis>
 | |
|             AND `mincha`<= <nCha>
 | |
|             AND `MinFortSave` <= <nFortSave>
 | |
|         */
 | |
| 
 | |
|         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 >= <nFileEnd>
 | |
|          */
 | |
|          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` = <race_feat***>) 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` = <cls_feat***>) 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++;
 | |
|     }
 | |
| }
 | |
| 
 |