Updated Release Archive. Fixed Mage-killer prereqs. Removed old LETO & ConvoCC related files. Added organized spell scroll store. Fixed Gloura spellbook. Various TLK fixes. Reorganized Repo. Removed invalid user folders. Added DocGen back in.
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++;
|
|
}
|
|
}
|
|
|