//:: Updated for .35 by Jaysyn 2023/03/10 ////////////////////////////////////////////////// /* Constants */ ////////////////////////////////////////////////// const string COHORT_DATABASE = "PRCCOHORTS"; const string COHORT_TAG = "prc_cohort"; //in the database there is the folloxing data structures: /* int CohortCount (total number of cohorts) object Cohort_X_obj (cohort itself) string Cohort_X_name (cohort name) int Cohort_X_race (cohort race) int Cohort_X_class1 (cohort class pos1) int Cohort_X_class2 (cohort class pos2) int Cohort_X_class3 (cohort class pos3) int Cohort_X_order (cohort law/chaos measure) int Cohort_X_moral (cohort good/evil measure) int Cohort_X_ethran (cohort has ethran feat) string Cohort_X_cdkey (cdkey of owning player) */ ////////////////////////////////////////////////// /* Function prototypes */ ////////////////////////////////////////////////// int GetMaximumCohortCount(object oPC); object GetCohort(int nID, object oPC); int GetCurrentCohortCount(object oPC); int GetCohortMaxLevel(int nLeadership, object oPC); void RegisterAsCohort(object oPC); object AddCohortToPlayer(int nCohortID, object oPC); void AddCohortToPlayerByObject(object oCohort, object oPC, int bDoSetup = TRUE); void RemoveCohortFromPlayer(object oCohort, object oPC); int GetLeadershipScore(object oPC = OBJECT_SELF); void CheckHB(object oPC); void AddPremadeCohortsToDB(); void StoreCohort(object oCohort); ////////////////////////////////////////////////// /* Includes */ ////////////////////////////////////////////////// #include "prc_feat_const" #include "nw_o2_coninclude" //#include "inc_utility" #include "inc_ecl" #include "inc_nwnx_funcs" //#include "pnp_shft_poly" //for DoRandomAppearance ////////////////////////////////////////////////// /* Function definitions */ ////////////////////////////////////////////////// object AddCohortToPlayer(int nCohortID, object oPC) { object oCohort = RetrieveCampaignObject(COHORT_DATABASE, "Cohort_"+IntToString(nCohortID)+"_obj", GetLocation(oPC)); //give it a tag AssignCommand(oCohort, SetIsDestroyable(TRUE, FALSE, FALSE)); DestroyObject(oCohort); oCohort = CopyObject(oCohort, GetLocation(oPC), OBJECT_INVALID, COHORT_TAG); SetLocalInt(oCohort, "CohortID", nCohortID); //pass it to the next function AddCohortToPlayerByObject(oCohort, oPC); return oCohort; } //changes portrait, head, and appearance //based on the target race with a degree of randomization. //This should only be used on NPCs, not players. void DoRandomAppearance(int nRace, object oTarget = OBJECT_SELF) { //store current appearance to be safe int nAppearance; //appearance to change into int nHeadMax; //max head ID, changed to random 1-max int nGender = GetGender(oTarget); int nPortraitMin;//minimum row in portraits.2da int nPortraitMax;//maximum row in portraits.2da switch(nRace) { case RACIAL_TYPE_DWARF: nAppearance = APPEARANCE_TYPE_DWARF; if(nGender == GENDER_MALE) { nHeadMax = 10; nPortraitMin = 9; nPortraitMax = 17; } else { nHeadMax = 12; nPortraitMin = 1; nPortraitMax = 8; } break; case RACIAL_TYPE_ELF: nAppearance = APPEARANCE_TYPE_ELF; if(nGender == GENDER_MALE) { nHeadMax = 10; nPortraitMin = 31; nPortraitMax = 40; } else { nHeadMax = 16; nPortraitMin = 18; nPortraitMax = 30; } break; case RACIAL_TYPE_HALFELF: nAppearance = APPEARANCE_TYPE_HALF_ELF; if(nGender == GENDER_MALE) { nHeadMax = 18; nPortraitMin = 93; nPortraitMax = 112; } else { nHeadMax = 15; nPortraitMin = 67; nPortraitMax = 92; } break; case RACIAL_TYPE_HALFORC: nAppearance = APPEARANCE_TYPE_HALF_ORC; if(nGender == GENDER_MALE) { nHeadMax = 11; nPortraitMin = 134; nPortraitMax = 139; } else { nHeadMax = 1; nPortraitMin = 130; nPortraitMax = 133; } break; case RACIAL_TYPE_HUMAN: nAppearance = APPEARANCE_TYPE_HUMAN; if(nGender == GENDER_MALE) { nHeadMax = 18; nPortraitMin = 93; nPortraitMax = 112; } else { nHeadMax = 15; nPortraitMin = 67; nPortraitMax = 92; } break; case RACIAL_TYPE_HALFLING: nAppearance = APPEARANCE_TYPE_HALFLING; if(nGender == GENDER_MALE) { nHeadMax = 8; nPortraitMin = 61; nPortraitMax = 66; } else { nHeadMax = 11; nPortraitMin = 54; nPortraitMax = 59; } break; case RACIAL_TYPE_GNOME: nAppearance = APPEARANCE_TYPE_GNOME; if(nGender == GENDER_MALE) { nHeadMax = 11; nPortraitMin = 47; nPortraitMax = 53; } else { nHeadMax = 9; nPortraitMin = 41; nPortraitMax = 46; } break; default: //not a normal race, abort return; } //change the appearance SetCreatureAppearanceType(oTarget, nAppearance); //need to be delayed a bit otherwise you get "supergnome" and "exploded elf" effects DelayCommand(1.1, SetCreatureBodyPart(CREATURE_PART_RIGHT_SHIN, d2(), oTarget)); DelayCommand(1.2, SetCreatureBodyPart(CREATURE_PART_LEFT_SHIN, d2(), oTarget)); DelayCommand(1.3, SetCreatureBodyPart(CREATURE_PART_RIGHT_THIGH, d2(), oTarget)); DelayCommand(1.4, SetCreatureBodyPart(CREATURE_PART_LEFT_THIGH, d2(), oTarget)); DelayCommand(1.5, SetCreatureBodyPart(CREATURE_PART_TORSO, d2(), oTarget)); DelayCommand(1.6, SetCreatureBodyPart(CREATURE_PART_RIGHT_FOREARM, d2(), oTarget)); DelayCommand(1.7, SetCreatureBodyPart(CREATURE_PART_LEFT_FOREARM, d2(), oTarget)); DelayCommand(1.8, SetCreatureBodyPart(CREATURE_PART_RIGHT_BICEP, d2(), oTarget)); DelayCommand(1.9, SetCreatureBodyPart(CREATURE_PART_LEFT_BICEP, d2(), oTarget)); //dont do these body parts, they dont have tattoos and weird things could happen //SetCreatureBodyPart(CREATURE_PART_BELT, d2(), oTarget); //SetCreatureBodyPart(CREATURE_PART_NECK, d2(), oTarget); //SetCreatureBodyPart(CREATURE_PART_RIGHT_SHOULDER, d2(), oTarget); //SetCreatureBodyPart(CREATURE_PART_LEFT_SHOULDER, d2(), oTarget); //SetCreatureBodyPart(CREATURE_PART_RIGHT_HAND, d2(), oTarget); //SetCreatureBodyPart(CREATURE_PART_LEFT_HAND, d2(), oTarget); //SetCreatureBodyPart(CREATURE_PART_PELVIS, d2(), oTarget); //SetCreatureBodyPart(CREATURE_PART_RIGHT_FOOT, d2(), oTarget); //SetCreatureBodyPart(CREATURE_PART_LEFT_FOOT, d2(), oTarget); //randomise the head DelayCommand(2.0, SetCreatureBodyPart(CREATURE_PART_HEAD, Random(nHeadMax)+1, oTarget)); //remove any wings/tails SetCreatureWingType(CREATURE_WING_TYPE_NONE, oTarget); SetCreatureTailType(CREATURE_TAIL_TYPE_NONE, oTarget); int nPortraitID = Random(nPortraitMax-nPortraitMin+1)+nPortraitMin; string sPortraitResRef = Get2DACache("portraits", "BaseResRef", nPortraitID); sPortraitResRef = GetStringLeft(sPortraitResRef, GetStringLength(sPortraitResRef)-1); //trim the trailing _ SetPortraitResRef(oTarget, sPortraitResRef); SetPortraitId(oTarget, nPortraitID); } void CancelGreatFeats(object oSpawn) { //store how many Great X feats they have //this is to fix a bioware bug where de-leveling doesnt remove the stat bonus int nGreatStr; int nGreatDex; int nGreatCon; int nGreatInt; int nGreatWis; int nGreatCha; if (GetHasFeat(FEAT_EPIC_GREAT_STRENGTH_10, oSpawn)) nGreatStr = 10; else if(GetHasFeat(FEAT_EPIC_GREAT_STRENGTH_9, oSpawn)) nGreatStr = 9; else if(GetHasFeat(FEAT_EPIC_GREAT_STRENGTH_8, oSpawn)) nGreatStr = 8; else if(GetHasFeat(FEAT_EPIC_GREAT_STRENGTH_7, oSpawn)) nGreatStr = 7; else if(GetHasFeat(FEAT_EPIC_GREAT_STRENGTH_6, oSpawn)) nGreatStr = 6; else if(GetHasFeat(FEAT_EPIC_GREAT_STRENGTH_5, oSpawn)) nGreatStr = 5; else if(GetHasFeat(FEAT_EPIC_GREAT_STRENGTH_4, oSpawn)) nGreatStr = 4; else if(GetHasFeat(FEAT_EPIC_GREAT_STRENGTH_3, oSpawn)) nGreatStr = 3; else if(GetHasFeat(FEAT_EPIC_GREAT_STRENGTH_2, oSpawn)) nGreatStr = 2; else if(GetHasFeat(FEAT_EPIC_GREAT_STRENGTH_1, oSpawn)) nGreatStr = 1; if (GetHasFeat(FEAT_EPIC_GREAT_DEXTERITY_10, oSpawn)) nGreatDex = 10; else if(GetHasFeat(FEAT_EPIC_GREAT_DEXTERITY_9, oSpawn)) nGreatDex = 9; else if(GetHasFeat(FEAT_EPIC_GREAT_DEXTERITY_8, oSpawn)) nGreatDex = 8; else if(GetHasFeat(FEAT_EPIC_GREAT_DEXTERITY_7, oSpawn)) nGreatDex = 7; else if(GetHasFeat(FEAT_EPIC_GREAT_DEXTERITY_6, oSpawn)) nGreatDex = 6; else if(GetHasFeat(FEAT_EPIC_GREAT_DEXTERITY_5, oSpawn)) nGreatDex = 5; else if(GetHasFeat(FEAT_EPIC_GREAT_DEXTERITY_4, oSpawn)) nGreatDex = 4; else if(GetHasFeat(FEAT_EPIC_GREAT_DEXTERITY_3, oSpawn)) nGreatDex = 3; else if(GetHasFeat(FEAT_EPIC_GREAT_DEXTERITY_2, oSpawn)) nGreatDex = 2; else if(GetHasFeat(FEAT_EPIC_GREAT_DEXTERITY_1, oSpawn)) nGreatDex = 1; if (GetHasFeat(FEAT_EPIC_GREAT_CONSTITUTION_10, oSpawn)) nGreatCon = 10; else if(GetHasFeat(FEAT_EPIC_GREAT_CONSTITUTION_9, oSpawn)) nGreatCon = 9; else if(GetHasFeat(FEAT_EPIC_GREAT_CONSTITUTION_8, oSpawn)) nGreatCon = 8; else if(GetHasFeat(FEAT_EPIC_GREAT_CONSTITUTION_7, oSpawn)) nGreatCon = 7; else if(GetHasFeat(FEAT_EPIC_GREAT_CONSTITUTION_6, oSpawn)) nGreatCon = 6; else if(GetHasFeat(FEAT_EPIC_GREAT_CONSTITUTION_5, oSpawn)) nGreatCon = 5; else if(GetHasFeat(FEAT_EPIC_GREAT_CONSTITUTION_4, oSpawn)) nGreatCon = 4; else if(GetHasFeat(FEAT_EPIC_GREAT_CONSTITUTION_3, oSpawn)) nGreatCon = 3; else if(GetHasFeat(FEAT_EPIC_GREAT_CONSTITUTION_2, oSpawn)) nGreatCon = 2; else if(GetHasFeat(FEAT_EPIC_GREAT_CONSTITUTION_1, oSpawn)) nGreatCon = 1; if (GetHasFeat(FEAT_EPIC_GREAT_INTELLIGENCE_10, oSpawn)) nGreatInt = 10; else if(GetHasFeat(FEAT_EPIC_GREAT_INTELLIGENCE_9, oSpawn)) nGreatInt = 9; else if(GetHasFeat(FEAT_EPIC_GREAT_INTELLIGENCE_8, oSpawn)) nGreatInt = 8; else if(GetHasFeat(FEAT_EPIC_GREAT_INTELLIGENCE_7, oSpawn)) nGreatInt = 7; else if(GetHasFeat(FEAT_EPIC_GREAT_INTELLIGENCE_6, oSpawn)) nGreatInt = 6; else if(GetHasFeat(FEAT_EPIC_GREAT_INTELLIGENCE_5, oSpawn)) nGreatInt = 5; else if(GetHasFeat(FEAT_EPIC_GREAT_INTELLIGENCE_4, oSpawn)) nGreatInt = 4; else if(GetHasFeat(FEAT_EPIC_GREAT_INTELLIGENCE_3, oSpawn)) nGreatInt = 3; else if(GetHasFeat(FEAT_EPIC_GREAT_INTELLIGENCE_2, oSpawn)) nGreatInt = 2; else if(GetHasFeat(FEAT_EPIC_GREAT_INTELLIGENCE_1, oSpawn)) nGreatInt = 1; if (GetHasFeat(FEAT_EPIC_GREAT_WISDOM_10, oSpawn)) nGreatWis = 10; else if(GetHasFeat(FEAT_EPIC_GREAT_WISDOM_9, oSpawn)) nGreatWis = 9; else if(GetHasFeat(FEAT_EPIC_GREAT_WISDOM_8, oSpawn)) nGreatWis = 8; else if(GetHasFeat(FEAT_EPIC_GREAT_WISDOM_7, oSpawn)) nGreatWis = 7; else if(GetHasFeat(FEAT_EPIC_GREAT_WISDOM_6, oSpawn)) nGreatWis = 6; else if(GetHasFeat(FEAT_EPIC_GREAT_WISDOM_5, oSpawn)) nGreatWis = 5; else if(GetHasFeat(FEAT_EPIC_GREAT_WISDOM_4, oSpawn)) nGreatWis = 4; else if(GetHasFeat(FEAT_EPIC_GREAT_WISDOM_3, oSpawn)) nGreatWis = 3; else if(GetHasFeat(FEAT_EPIC_GREAT_WISDOM_2, oSpawn)) nGreatWis = 2; else if(GetHasFeat(FEAT_EPIC_GREAT_WISDOM_1, oSpawn)) nGreatWis = 1; if (GetHasFeat(FEAT_EPIC_GREAT_CHARISMA_10, oSpawn)) nGreatCha = 10; else if(GetHasFeat(FEAT_EPIC_GREAT_CHARISMA_9, oSpawn)) nGreatCha = 9; else if(GetHasFeat(FEAT_EPIC_GREAT_CHARISMA_8, oSpawn)) nGreatCha = 8; else if(GetHasFeat(FEAT_EPIC_GREAT_CHARISMA_7, oSpawn)) nGreatCha = 7; else if(GetHasFeat(FEAT_EPIC_GREAT_CHARISMA_6, oSpawn)) nGreatCha = 6; else if(GetHasFeat(FEAT_EPIC_GREAT_CHARISMA_5, oSpawn)) nGreatCha = 5; else if(GetHasFeat(FEAT_EPIC_GREAT_CHARISMA_4, oSpawn)) nGreatCha = 4; else if(GetHasFeat(FEAT_EPIC_GREAT_CHARISMA_3, oSpawn)) nGreatCha = 3; else if(GetHasFeat(FEAT_EPIC_GREAT_CHARISMA_2, oSpawn)) nGreatCha = 2; else if(GetHasFeat(FEAT_EPIC_GREAT_CHARISMA_1, oSpawn)) nGreatCha = 1; //apply penalties to counter the GreatX feats if(GetPRCSwitch(PRC_NWNX_FUNCS)) { if(nGreatStr) PRC_Funcs_ModAbilityScore(oSpawn, ABILITY_STRENGTH, -nGreatStr); if(nGreatDex) PRC_Funcs_ModAbilityScore(oSpawn, ABILITY_DEXTERITY, -nGreatDex); if(nGreatCon) PRC_Funcs_ModAbilityScore(oSpawn, ABILITY_CONSTITUTION, -nGreatCon); if(nGreatInt) PRC_Funcs_ModAbilityScore(oSpawn, ABILITY_INTELLIGENCE, -nGreatInt); if(nGreatWis) PRC_Funcs_ModAbilityScore(oSpawn, ABILITY_WISDOM, -nGreatWis); if(nGreatCha) PRC_Funcs_ModAbilityScore(oSpawn, ABILITY_CHARISMA, -nGreatCha); } else { if(nGreatStr) ApplyEffectToObject(DURATION_TYPE_PERMANENT, SupernaturalEffect(EffectAbilityDecrease(ABILITY_STRENGTH, nGreatStr)), oSpawn); if(nGreatDex) ApplyEffectToObject(DURATION_TYPE_PERMANENT, SupernaturalEffect(EffectAbilityDecrease(ABILITY_DEXTERITY, nGreatDex)), oSpawn); if(nGreatCon) ApplyEffectToObject(DURATION_TYPE_PERMANENT, SupernaturalEffect(EffectAbilityDecrease(ABILITY_CONSTITUTION, nGreatCon)), oSpawn); if(nGreatInt) ApplyEffectToObject(DURATION_TYPE_PERMANENT, SupernaturalEffect(EffectAbilityDecrease(ABILITY_INTELLIGENCE, nGreatInt)), oSpawn); if(nGreatWis) ApplyEffectToObject(DURATION_TYPE_PERMANENT, SupernaturalEffect(EffectAbilityDecrease(ABILITY_WISDOM, nGreatWis)), oSpawn); if(nGreatCha) ApplyEffectToObject(DURATION_TYPE_PERMANENT, SupernaturalEffect(EffectAbilityDecrease(ABILITY_CHARISMA, nGreatCha)), oSpawn); } } void AddCohortToPlayerByObject(object oCohort, object oPC, int bDoSetup = TRUE) { //add it to the pc int nMaxHenchmen = GetMaxHenchmen(); SetMaxHenchmen(99); AddHenchman(oPC, oCohort); SetMaxHenchmen(nMaxHenchmen); object oSkin = GetPCSkin(oCohort); if(bDoSetup) { //if it was a premade one, give it a random name //randomize its appearance using DoRandomAppearance if(GetResRef(oCohort) != "") { string sName; //first name switch(MyPRCGetRacialType(oCohort)) { case RACIAL_TYPE_DWARF: if(GetGender(oCohort) == GENDER_FEMALE) sName += RandomName(NAME_FIRST_DWARF_FEMALE); else sName += RandomName(NAME_FIRST_DWARF_MALE); break; case RACIAL_TYPE_ELF: if(GetGender(oCohort) == GENDER_FEMALE) sName += RandomName(NAME_FIRST_ELF_FEMALE); else sName += RandomName(NAME_FIRST_ELF_MALE); break; case RACIAL_TYPE_GNOME: if(GetGender(oCohort) == GENDER_FEMALE) sName += RandomName(NAME_FIRST_GNOME_FEMALE); else sName += RandomName(NAME_FIRST_GNOME_MALE); break; case RACIAL_TYPE_HUMAN: if(GetGender(oCohort) == GENDER_FEMALE) sName += RandomName(NAME_FIRST_HUMAN_FEMALE); else sName += RandomName(NAME_FIRST_HUMAN_MALE); break; case RACIAL_TYPE_HALFELF: if(GetGender(oCohort) == GENDER_FEMALE) sName += RandomName(NAME_FIRST_HALFELF_FEMALE); else sName += RandomName(NAME_FIRST_HALFELF_MALE); break; case RACIAL_TYPE_HALFORC: if(GetGender(oCohort) == GENDER_FEMALE) sName += RandomName(NAME_FIRST_HALFORC_FEMALE); else sName += RandomName(NAME_FIRST_HALFORC_MALE); break; case RACIAL_TYPE_HALFLING: if(GetGender(oCohort) == GENDER_FEMALE) sName += RandomName(NAME_FIRST_HALFLING_FEMALE); else sName += RandomName(NAME_FIRST_HALFLING_MALE); break; } sName += " "; //surname switch(MyPRCGetRacialType(oCohort)) { case RACIAL_TYPE_DWARF: sName += RandomName(NAME_LAST_DWARF); break; case RACIAL_TYPE_ELF: sName += RandomName(NAME_LAST_ELF); break; case RACIAL_TYPE_GNOME: sName += RandomName(NAME_LAST_GNOME); break; case RACIAL_TYPE_HUMAN: sName += RandomName(NAME_LAST_HUMAN); break; case RACIAL_TYPE_HALFELF: sName += RandomName(NAME_LAST_HALFELF); break; case RACIAL_TYPE_HALFORC: sName += RandomName(NAME_LAST_HALFORC); break; case RACIAL_TYPE_HALFLING: sName += RandomName(NAME_LAST_HALFLING); break; } //sanity check if(sName == " ") sName = ""; //change the name AssignCommand(oCohort, SetName(oCohort, sName)); //use disguise code to alter head etc DoRandomAppearance(MyPRCGetRacialType(oCohort), oCohort); //DoRandomAppearance removed wings/tails need to re-add if(GetRacialType(oCohort) == RACIAL_TYPE_FEYRI) SetCreatureWingType(CREATURE_WING_TYPE_DEMON, oCohort); else if(GetRacialType(oCohort) == RACIAL_TYPE_AVARIEL) SetCreatureWingType(CREATURE_WING_TYPE_BIRD, oCohort); else if(GetRacialType(oCohort) == RACIAL_TYPE_GLOURA) SetCreatureWingType(CREATURE_WING_TYPE_BUTTERFLY, oCohort); } //if its a custom made cohort, need to cancel GreatX feats else CancelGreatFeats(oCohort); //set it to the pcs level int nLevel = GetCohortMaxLevel(GetLeadershipScore(oPC), oPC); SetXP(oCohort, nLevel*(nLevel-1)*500); SetLocalInt(oCohort, "MastersXP", GetXP(oPC)); DelayCommand(1.0, AssignCommand(oCohort, SetIsDestroyable(FALSE, TRUE, TRUE))); DelayCommand(1.0, AssignCommand(oCohort, SetLootable(oCohort, TRUE))); //set its maximum level lag if(GetCurrentCohortCount(oPC) <= GetPRCSwitch(PRC_BONUS_COHORTS)) { //bonus cohort, no cap } else if(GetPRCSwitch(PRC_THRALLHERD_LEADERSHIP) && GetLevelByClass(CLASS_TYPE_THRALLHERD, oPC) && GetCurrentCohortCount(oPC) <= GetPRCSwitch(PRC_BONUS_COHORTS)+1) { //thrallherd with switch, 1 level lag SetLocalInt(oCohort, "CohortLevelLag", 1); } else if(GetPRCSwitch(PRC_THRALLHERD_LEADERSHIP) && GetLevelByClass(CLASS_TYPE_THRALLHERD, oPC) >= 10 && GetCurrentCohortCount(oPC) <= GetPRCSwitch(PRC_BONUS_COHORTS)+2) { //twofold master with switch, 2 level lag SetLocalInt(oCohort, "CohortLevelLag", 2); } else { //other cohort have a 2 level lag SetLocalInt(oCohort, "CohortLevelLag", 2); } //strip its equipment & inventory object oTest = GetFirstItemInInventory(oCohort); object oToken = GetHideToken(oCohort); while(GetIsObjectValid(oTest)) { if(GetHasInventory(oTest)) { object oTest2 = GetFirstItemInInventory(oTest); while(GetIsObjectValid(oTest2)) { // Avoid blowing up the hide and token that just had the eventscripts stored on them if(oTest2 != oSkin && oTest2 != oToken) DestroyObject(oTest2); oTest2 = GetNextItemInInventory(oTest); } } // Avoid blowing up the hide and token that just had the eventscripts stored on them if(oTest != oSkin && oTest != oToken) DestroyObject(oTest); oTest = GetNextItemInInventory(oCohort); } int nSlot; for(nSlot = 0;nSlot<14;nSlot++) { oTest = GetItemInSlot(nSlot, oCohort); DestroyObject(oTest); } //get rid of any gold it has TakeGoldFromCreature(GetGold(oCohort), oCohort, TRUE); } //clean up any leftovers on the skin ScrubPCSkin(oCohort, oSkin); DeletePRCLocalInts(oSkin); //turn on its scripts //normal MoB set AddEventScript(oCohort, EVENT_VIRTUAL_ONPHYSICALATTACKED, "prc_ai_mob_attck", TRUE, FALSE); AddEventScript(oCohort, EVENT_VIRTUAL_ONBLOCKED, "prc_ai_mob_block", TRUE, FALSE); AddEventScript(oCohort, EVENT_VIRTUAL_ONCOMBATROUNDEND, "prc_ai_mob_combt", TRUE, FALSE); AddEventScript(oCohort, EVENT_VIRTUAL_ONDAMAGED, "prc_ai_mob_damag", TRUE, FALSE); AddEventScript(oCohort, EVENT_VIRTUAL_ONDISTURBED, "prc_ai_mob_distb", TRUE, FALSE); AddEventScript(oCohort, EVENT_VIRTUAL_ONPERCEPTION, "prc_ai_mob_percp", TRUE, FALSE); AddEventScript(oCohort, EVENT_VIRTUAL_ONSPAWNED, "prc_ai_mob_spawn", TRUE, FALSE); AddEventScript(oCohort, EVENT_VIRTUAL_ONSPELLCASTAT, "prc_ai_mob_spell", TRUE, FALSE); AddEventScript(oCohort, EVENT_VIRTUAL_ONDEATH, "prc_ai_mob_death", TRUE, FALSE); AddEventScript(oCohort, EVENT_VIRTUAL_ONRESTED, "prc_ai_mob_rest", TRUE, FALSE); AddEventScript(oCohort, EVENT_VIRTUAL_ONUSERDEFINED, "prc_ai_mob_userd", TRUE, FALSE); //dont run this, cohort-specific script replaces it //AddEventScript(oCohort, EVENT_VIRTUAL_ONCONVERSATION, "prc_ai_mob_conv", TRUE, TRUE); AddEventScript(oCohort, EVENT_VIRTUAL_ONHEARTBEAT, "prc_ai_mob_heart", TRUE, FALSE); //cohort specific ones AddEventScript(oCohort, EVENT_VIRTUAL_ONCONVERSATION, "prc_ai_coh_conv", TRUE, FALSE); AddEventScript(oCohort, EVENT_VIRTUAL_ONHEARTBEAT, "prc_ai_coh_hb", TRUE, FALSE); //mark the master on the cohort SetLocalObject(oCohort, "MasterObject", oPC); //DEBUG //various tests if (DEBUG) DoDebug("Cohort Name="+GetName(oCohort)); if (DEBUG) DoDebug("Cohort HD="+IntToString(GetHitDice(oCohort))); if (DEBUG) DoDebug("Cohort XP="+IntToString(GetXP(oCohort))); if (DEBUG) DoDebug("Cohort GetIsPC="+IntToString(GetIsPC(oCohort))); // And now gear it up if (!GetPRCSwitch(PRC_DISABLE_COHORT_STARTING_GEAR)) { int i; int nHD = GetHitDice(oCohort); for(i = 0;i= 3) nLeadership += GetLevelByClass(CLASS_TYPE_SHADOW_THIEF_AMN, oPC) - 2; //without epic leadership its capped at 25 if(!GetHasFeat(FEAT_EPIC_LEADERSHIP, oPC) && nLeadership > 25) nLeadership = 25; return nLeadership; } void StoreCohort(object oCohort) { int nCohortCount = GetCampaignInt(COHORT_DATABASE, "CohortCount"); int i; for(i=0;i 1) return; SetLocalInt(oPC, "CohortCheckHB", GetLocalInt(oPC, "CohortCheckHB")+1); DelayCommand(0.99, SetLocalInt(oPC, "CohortCheckHB", GetLocalInt(oPC, "CohortCheckHB")-1)); SetCommandable(FALSE, oPC); if(GetHitDice(oPC) == 40) { StoreCohort(oPC); //restore previous xp amound SetXP(oPC, GetLocalInt(oPC, "OriginalXP")); //tell the player what was done SendMessageToPC(oPC, "Character registered as cohort."); //remove the non-commandabiltiy SetCommandable(TRUE, oPC); // Clean up DeletePersistantLocalInt(oPC, "RegisteringAsCohort"); DeleteLocalInt(oPC, "OriginalXP"); //stop the psuedoHB return; } DelayCommand(1.0, CheckHB(oPC)); } void RegisterAsCohort(object oPC) { string sMessage; sMessage += "This will register you character to be selected as a cohort.\n"; sMessage += "As part of this process, you have to levelup to level 40.\n"; sMessage += "Once you reach level 40, your character will be stored.\n"; sMessage += "Then when the character is used as a cohort, it will follow that levelup path.\n"; sMessage += "Any changes to the cohort will not apply to the original character.\n"; //SendMessageToPC(oPC, sMessage); FloatingTextStringOnCreature(sMessage, oPC); SetLocalInt(oPC, "OriginalXP", GetXP(oPC)); SetXP(oPC, 40*(40-1)*500); SetPersistantLocalInt(oPC, "RegisteringAsCohort", TRUE); AssignCommand(GetModule(), CheckHB(oPC)); } int LeadershipScore2CohortLevel(int nLeadership) { switch(nLeadership) { case 1: return 0; case 2: return 1; case 3: return 2; case 4: case 5: return 3; case 6: return 4; case 7: case 8: return 5; case 9: return 6; case 10: case 11: return 7; case 12: return 8; case 13: return 9; case 14: case 15: return 10; case 16: return 11; case 17: case 18: return 12; case 19: return 13; case 20: return 14; case 21: case 22: return 15; case 23: return 16; case 24: case 25: return 17; case 26: case 27: return 18; case 28: case 29: return 19; case 30: case 31: return 20; case 32: case 33: return 21; case 34: case 35: return 22; case 36: case 37: return 23; case 38: case 39: return 24; case 40: case 41: return 25; case 42: case 43: return 26; case 44: case 45: return 27; case 46: case 47: return 28; case 48: case 49: return 29; case 50: case 51: return 30; case 52: case 53: return 31; case 54: case 55: return 32; case 56: case 57: return 33; case 58: case 59: return 34; case 60: return 35; case 61: case 62: return 36; case 63: case 64: return 37; case 65: case 66: return 38; case 67: case 68: return 39; case 69: case 70: return 40; } return 0; } int GetCohortMaxLevel(int nLeadership, object oPC) { //if its a bonus cohort, use the players ECL int nMasterLevel = GetECL(oPC); if(GetCurrentCohortCount(oPC) <= GetPRCSwitch(PRC_BONUS_COHORTS)) return nMasterLevel; int nLevel = LeadershipScore2CohortLevel(nLeadership); //apply a level lag if(GetPRCSwitch(PRC_THRALLHERD_LEADERSHIP) && GetLevelByClass(CLASS_TYPE_THRALLHERD, oPC) && GetCurrentCohortCount(oPC) <= GetPRCSwitch(PRC_BONUS_COHORTS)+1) { //thrallherd with switch, 1 level lag if(nLevel > nMasterLevel-1) nLevel = nMasterLevel-1; } else if(GetPRCSwitch(PRC_THRALLHERD_LEADERSHIP) && GetLevelByClass(CLASS_TYPE_THRALLHERD, oPC) >= 10 && GetCurrentCohortCount(oPC) <= GetPRCSwitch(PRC_BONUS_COHORTS)+2) { //twofold master with switch, 2 level lag if(nLevel > nMasterLevel-2) nLevel = nMasterLevel-2; } else { //other cohort have a 2 level lag if(nLevel > nMasterLevel-2) nLevel = nMasterLevel-2; if (GetHasFeat(FEAT_IMPROVED_COHORT, oPC)) nLevel += 1; } //really, leadership should be capped at 25 / 17HD //but this is a sanity check if(nLevel > 20 && !GetHasFeat(FEAT_EPIC_LEADERSHIP, oPC)) nLevel = 20; return nLevel; } int GetCurrentCohortCount(object oPC) { int nCount; object oTest; object oOldTest; int i = 1; oTest = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, i); while(GetIsObjectValid(oTest) && oTest != oOldTest) { if(GetTag(oTest) == COHORT_TAG) nCount++; i++; oOldTest = oTest; oTest = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, i); } return nCount; } object GetCohort(int nID, object oPC) { int nCount; object oTest; object oOldTest; int i = 1; oTest = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, i); while(GetIsObjectValid(oTest) && oTest != oOldTest) { if(GetTag(oTest) == COHORT_TAG) nCount++; if(nCount == nID) return oTest; i++; oOldTest = oTest; oTest = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, i); } return OBJECT_INVALID; } int GetMaximumCohortCount(object oPC) { int nCount; if(!GetLevelByClass(CLASS_TYPE_THRALLHERD, oPC)) { if(GetHasFeat(FEAT_LEADERSHIP, oPC)) nCount++; if(GetHasFeat(FEAT_LEGENDARY_COMMANDER, oPC)) nCount++; } //thrallherd with switch else if(GetPRCSwitch(PRC_THRALLHERD_LEADERSHIP)) { nCount++; //twofold masteer if(GetLevelByClass(CLASS_TYPE_THRALLHERD, oPC) > 9) nCount++; } //hathran class if(GetHasFeat(FEAT_HATH_COHORT, oPC)) nCount++; //orc warlord with switch if(GetHasFeat(FEAT_GATHER_HORDE_I, oPC) && GetPRCSwitch(PRC_ORC_WARLORD_COHORT)) nCount++; nCount += GetPRCSwitch(PRC_BONUS_COHORTS); return nCount; } int GetIsCohortChoiceValid(string sName, int nRace, int nClass1, int nClass2, int nClass3, int nOrder, int nMoral, int nEthran, string sKey, int nDeleted, object oPC) { //has been deleted if(nDeleted) { DoDebug("GetIsCohortChoiceValid() is FALSE because cohort had been deleted"); return FALSE; } int bIsValid = TRUE; int nCohortCount = GetMaximumCohortCount(oPC); int i; //another players cohort if(GetPCPublicCDKey(oPC) != "" && GetPCPublicCDKey(oPC) != sKey) { DoDebug("GetIsCohortChoiceValid() is FALSE because cdkey is incorrect"); bIsValid = FALSE; } //is character if(bIsValid && GetName(oPC) == sName) { DoDebug("GetIsCohortChoiceValid() is FALSE because name is in use"); bIsValid = FALSE; } //is already a cohort if(bIsValid && sName != "") { for(i=1;i<=nCohortCount;i++) { object oCohort = GetCohort(i, oPC); if(GetName(oCohort) == sName) { DoDebug("GetIsCohortChoiceValid() is FALSE because cohort is already in use."); bIsValid = FALSE; } } } //hathran if(bIsValid && GetHasFeat(FEAT_HATH_COHORT, oPC)) { int nEthranBarbarianCount = 0; for(i=1;i<=nCohortCount;i++) { object oCohort = GetCohort(i, oPC); if(GetIsObjectValid(oCohort) &&(GetHasFeat(FEAT_HATH_COHORT, oCohort) || GetLevelByClass(CLASS_TYPE_BARBARIAN, oCohort))) nEthranBarbarianCount++; } //must have at least one ethran or barbarian if(!nEthranBarbarianCount && GetCurrentCohortCount(oPC) >= GetMaximumCohortCount(oPC)-1 && !nEthran && nClass1 != CLASS_TYPE_BARBARIAN && nClass2 != CLASS_TYPE_BARBARIAN && nClass3 != CLASS_TYPE_BARBARIAN) bIsValid = FALSE; } //OrcWarlord if(bIsValid && GetHasFeat(FEAT_GATHER_HORDE_I, oPC) && GetPRCSwitch(PRC_ORC_WARLORD_COHORT)) { int nOrcCount = 0; for(i=1;i<=nCohortCount;i++) { object oCohort = GetCohort(i, oPC); if(GetIsObjectValid(oCohort) && (MyPRCGetRacialType(oCohort) == RACIAL_TYPE_HUMANOID_ORC || MyPRCGetRacialType(oCohort) == RACIAL_TYPE_HALFORC)) nOrcCount++; } //must have at least one orc if(!nOrcCount && GetCurrentCohortCount(oPC) >= GetMaximumCohortCount(oPC)-1 && nRace != RACIAL_TYPE_HUMANOID_ORC && nRace != RACIAL_TYPE_HALFORC && nRace != RACIAL_TYPE_GRAYORC && nRace != RACIAL_TYPE_OROG && nRace != RACIAL_TYPE_TANARUKK ) bIsValid = FALSE; } //Undead Leadership //Wild Cohort //not implemented yet //return result return bIsValid; } int GetIsCohortChoiceValidByID(int nID, object oPC) { string sID = IntToString(nID); string sName = GetCampaignString( COHORT_DATABASE, "Cohort_"+sID+"_name"); int nRace = GetCampaignInt( COHORT_DATABASE, "Cohort_"+sID+"_race"); int nClass1=GetCampaignInt( COHORT_DATABASE, "Cohort_"+sID+"_class1"); int nClass2=GetCampaignInt( COHORT_DATABASE, "Cohort_"+sID+"_class2"); int nClass3=GetCampaignInt( COHORT_DATABASE, "Cohort_"+sID+"_class3"); int nOrder= GetCampaignInt( COHORT_DATABASE, "Cohort_"+sID+"_order"); int nMoral= GetCampaignInt( COHORT_DATABASE, "Cohort_"+sID+"_moral"); int nEthran=GetCampaignInt( COHORT_DATABASE, "Cohort_"+sID+"_ethran"); string sKey = GetCampaignString( COHORT_DATABASE, "Cohort_"+sID+"_cdkey"); int nDeleted = GetCampaignInt(COHORT_DATABASE, "Cohort_"+sID+"_deleted"); return GetIsCohortChoiceValid(sName, nRace, nClass1, nClass2, nClass3, nOrder, nMoral, nEthran, sKey, nDeleted, oPC); } int GetCanRegister(object oPC) { int bReturn = TRUE; int i; int nCohortCount = GetCampaignInt(COHORT_DATABASE, "CohortCount"); for(i=0;i