I create the NPC in the Oblivion area, and then choose a random location within the area to which they should be jumped. That reduces one of the ActionJumpToLocation calls which can cause a NPC not to equip their clothing. After creating them in Oblivion, I have a looping 1.5 second check to jump them back which at the bottom of this section of code.
void RandomizeNPC(object oDynamicNPC, string sMonsterRace = "")
{
int iGender = GetGender(oDynamicNPC);
int iRace = GetLocalInt(oDynamicNPC, "RacialType"); //Allow racial overrides to be set on the NPC (see qst_ar_onenter for an example)
if (!iRace) iRace = GetRacialType(oDynamicNPC);
if (GetIsPlayableRacialType(oDynamicNPC)) {
//Select a random appearance (head, default vs. predefined, clothes, phenotype) for the NPC.
SetRandomAppearance(oDynamicNPC, iRace, iGender);
//Next, randomize the clothing for the NPC. This sets the RandomClothing variable on the oDynamicNPC
SetRandomClothes(oDynamicNPC);
//Next, set random hair color for the NPC
DelayCommand(0.3, SetRandomHairColor(oDynamicNPC, iRace));
//Next, set random skin color for the NPC
DelayCommand(0.3, SetRandomSkinColor(oDynamicNPC, iRace));
//Next, set random tattoo colors for the NPC
DelayCommand(0.3, SetRandomTattooColor(oDynamicNPC));
//Next, assign a Random Portrait to the NPC
DelayCommand(0.3, SetRandomPortraitID(oDynamicNPC, iRace, iGender));
//Next, if not a specified hostile NPC, make sure they are of the commoner faction
if (!GetLocalInt(oDynamicNPC, "IsHostile")) ChangeToStandardFaction(oDynamicNPC, STANDARD_FACTION_COMMONER);
//Randomly Adjust Alignment. All standard race templates start at True Neutral
if (!GetLevelByClass(CLASS_TYPE_PALADIN, oDynamicNPC)) { //Don't adjust paladin alignment
if (d10() <= 6) AdjustAlignment(oDynamicNPC, ALIGNMENT_EVIL, Random(51)); //Skew more towards evil than good
else AdjustAlignment(oDynamicNPC, ALIGNMENT_GOOD, Random(51));
if (d10() <= 5) AdjustAlignment(oDynamicNPC, ALIGNMENT_LAWFUL, Random(51)); //Equal chance they are adjusted to Lawful or Chaos
else AdjustAlignment(oDynamicNPC, ALIGNMENT_CHAOTIC, Random(51));
}
}
//Else if a non-playable race, just select portraits and various appearances if applicable (gnolls, orcs, goblins, other monsters, etc)
else {
if (sMonsterRace == "Goblin") {
SetCreatureAppearanceType(oDynamicNPC, 82+Random(6)); //Racial appearance types for goblins in nwscript range between 82-87
SetPortraitId(oDynamicNPC, 219+Random(7)); //Racial portraits for goblins in portraits.2da range between 219-225
}
else if (sMonsterRace == "Orc") {
SetCreatureAppearanceType(oDynamicNPC, 136+Random(6)); //Racial appearance types for orcs in nwscript range between 136-141
SetPortraitId(oDynamicNPC, 271+Random(4)); //Racial portraits for orcs in portraits.2da range between 271-274
}
else if (sMonsterRace == "Gnoll") {
SetCreatureAppearanceType(oDynamicNPC, 388+Random(2)); //Racial appearance types for gnolls in nwscript range between 388-389
SetPortraitId(oDynamicNPC, 604+Random(2)); //Racial portraits for gnolls in portraits.2da range between 604-605
}
}
DelayCommand(1.5, GetIsReadyToTransport(oDynamicNPC)); //After a short period of time, check if the NPC is ready to jump back to the playable area
}
This is the looping check with extra Thay-specific stuff in it that you don't need, but hopefully it gives you an idea. This is the safety valve to try and get the NPC out of the inaccessible area.
void GetIsReadyToTransport(object oDynamicNPC, int iTimesChecked=0)
{
if (!GetIsObjectValid(oDynamicNPC)) return; //If not valid, end here
object oArea = GetArea(oDynamicNPC);
if (!GetIsObjectValid(oArea)) { //Delay the check by one second if the area is still loading
DelayCommand(1.0, GetIsReadyToTransport(oDynamicNPC));
return;
}
//If already run once and they are now out of the creation area, end here
if (iTimesChecked && oArea != GetAreaFromLocation(GetLocation(GetObjectByTag("WP_RandomNPCCreation")))) return;
//10 iterations and still can't get this NPC out of the creation area - they're not getting out for some reason. Send alert, end here, and destroy them
if (iTimesChecked > 10) {
WriteTimestampedLogEntry("NPC ALERT: "+GetName(oDynamicNPC)+"/"+GetTag(oDynamicNPC)+" was stuck in the NPC creation area. It was destroyed.");
DestroyObject(oDynamicNPC);
return;
}
//Check if they have a regular race appearance. If they do not have clothing equipped, check if there is valid clothing yet created on them and try to equip it, and will check the next heartbeat
int iEquippingClothes = FALSE;
int iAppearance = GetAppearanceType(oDynamicNPC);
if ((iAppearance >= 0 && iAppearance <= 6) && GetItemInSlot(INVENTORY_SLOT_CHEST, oDynamicNPC) == OBJECT_INVALID && GetTag(oDynamicNPC) != "Legionnaire") {
object oClothing = GetLocalObject(oDynamicNPC, "RandomClothing");
if (oClothing == OBJECT_INVALID) { //Check if they have template clothing
oClothing = GetItemPossessedBy(oDynamicNPC, "TemplateClothing");
SetLocalObject(oDynamicNPC, "RandomClothing", oClothing);
}
if (oClothing == OBJECT_INVALID) { //If neither random or template clothing, create something on them
WriteTimestampedLogEntry("NPC ALERT: "+GetName(oDynamicNPC)+"/"+GetTag(oDynamicNPC)+" did not have random clothing and had nothing in chest slot. Created clothing on them.");
oClothing = CreateItemOnObject("clothingtemplate", oDynamicNPC);
SetLocalObject(oDynamicNPC, "RandomClothing", oClothing);
}
iEquippingClothes = TRUE;
DelayCommand(0.3, AssignCommand(oDynamicNPC, ClearAllActions(TRUE)));
DelayCommand(0.4, AssignCommand(oDynamicNPC, ActionEquipItem(oClothing,INVENTORY_SLOT_CHEST)));
}
//Jump the NPC back to the game world
SetCommandable(TRUE, oDynamicNPC);
location lNewLoc = GetLocalLocation(oDynamicNPC, "DynamicNPCLocation");
if (GetLocalObject(oDynamicNPC, "DynamicSpawnArea") != OBJECT_INVALID && GetAreaFromLocation(lNewLoc) == OBJECT_INVALID) lNewLoc = RandomLocation(GetLocalObject(oDynamicNPC, "DynamicSpawnArea"), TRUE);
if (!iEquippingClothes) DelayCommand(0.5, AssignCommand(oDynamicNPC, ClearAllActions(TRUE)));
DelayCommand(1.0, AssignCommand(oDynamicNPC, JumpToLocation(lNewLoc)));
DelayCommand(1.5, GetIsReadyToTransport(oDynamicNPC, iTimesChecked+1));
}
Obviously it's not perfect, and with 1, 3, 5+ second delays the NPCs sometimes magically appear in front of PCs if their random location in the playable area is nearby them. But overall, this has signficantly reduced randomized part-less NPCs in randomized clothing.