Jump to content
  • 0

Automating properly disabled objects


Katarsi

Question

There are mods that just slap "Initially Disabled" as a record flag when they want to disable certain objects (clutter, flora), and call it a day - which is not a good practice. PlayerRef is missing, with "set enable state to opposite of parent", as well as Z axis at -30000. AFAIK, this is a proper practice when you want to correctly disable an object.

Instead of going through every single object that has this shortcoming, and correcting (patching) it manually, is there a chance of a xEdit script that could automate this process? As in, click on a plugin, apply the script, and it assigns the missing values to initially disabled objects that don't have them. It would save hours of work.

Edited by Katarsi
Link to comment
Share on other sites

12 answers to this question

Recommended Posts

  • 0

Slightly better and safer version which skips Initially Disabled references if they already have an Enable Parent:

image.pngimage.png

As discussed above, the script doesn't check if the reference might be used any other way by the plugin, which would require cross-referencing the reference in the plugin. I'm not willing to spend more than 10 minutes on this, so this is the best you'll get.

Link to comment
Share on other sites

  • 0
6 hours ago, Katarsi said:

AFAIK, this is a proper practice when you want to correctly disable an object.

This is true when the goal is to permanently disable an object not used by the mod in order to completely remove it from the world. There are other cases where a mod may initially disable an object, which may later be re-enabled:

  • By a Papyrus script.
  • By an Enable Parent.

In these cases, setting Enable Parent opposite of Player and Z to -30000 would break the mod.

So an automated procedure is not possible without analyzing how each initially disabled object might be used, or not, by the mod.

In the worst case, an initially disabled object might be enabled by a Papyrus script but its usage be "hidden" from the mod plugin, because the script hardcodes the RefID of the object instead of passing it as a script property. This is a very bad practice, which fortunately is uncommon nowadays, but it exists, particularly in old mods. This case cannot be determined without reading the Papyrus script source code.

So if you've determined and are absolutely sure that the mod doesn't use the object at all and it doesn't re-enable it by any means - which is by itself a time-consuming process - you can flag it in xEdit as Deleted in its record header. Once you're done with all the Initially Disabled objects, you can just run the plugin through xEdit QuickAutoClean which will automatically change Deleted objects into Initially Disabled + Enable Parent opposite Player + Z = 30000.

Link to comment
Share on other sites

  • 0
21 minutes ago, Mousetick said:

So if you've determined and are absolutely sure that the mod doesn't use the object at all and it doesn't re-enable it by any means - which is by itself a time-consuming process - you can flag it in xEdit as Deleted in its record header. Once you're done with all the Initially Disabled objects, you can just run the plugin through xEdit QuickAutoClean which will automatically change Deleted objects into Initially Disabled + Enable Parent opposite Player + Z = 30000.

I know what you're talking about, I know the difference and I'm 100% sure that the placed objects I want to remove will not be used by a script later. Namely, I'm talking about trees. Not even tree objects that have activators on them (such as logs with mora tapinella and aspen stumps with scaly pholiota), but just plain ol' trees.

And flagging them with Delete means that I have to go through every record either way, which doesn't mean much in the sense of time and effort invested in the whole thing.

It would have been nice to have a script that could be applied to a single plugin and just add these records to Placed Objects which only have Initially Disabled on them. "Use at your own risk", type of script for xEdit. I would immensely appreciate it, if someone knew how to make it, and it would save me So. Much. Time.

Link to comment
Share on other sites

  • 0

The context wasn't clear in your OP. You may know exactly what you're doing but random readers may not and could get the wrong idea.

49 minutes ago, Katarsi said:

It would have been nice to have a script that could be applied to a single plugin and just add these records to Placed Objects which only have Initially Disabled on them. "Use at your own risk", type of script for xEdit. I would immensely appreciate it, if someone knew how to make it, and it would save me So. Much. Time.

You can do it yourself, it's not very complicated.

  • Copy the existing Undelete and Disable References.pas to Permanently Disable Initially Disabled References.pas.
  • Edit Permanently Disable Initially Disabled References.pas as follows (left side is original script, right side is edited version):

image.pngimage.png

Only the differences are shown above, everything else remains the same.

  • Load the plugin in xEdit.
  • Right-click on the plugin, select 'Apply Script', choose 'Permanently Disable Initially Disabled References', click OK.
  • Done. Save plugin.

The messages window will display the list of modified references, for example:

Spoiler
[00:00] Start: Applying script "Permanently Disable Initially Disabled References"
Permanently disabling: [REFR:FE001B96] (places _ArtCustomBedSingleR "Bed" [FURN:FE001951] in GRUP Cell Temporary Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: [REFR:FE001ADF] (places CommonBench01 "Bench" [FURN:0002E6CF] in GRUP Cell Temporary Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: [REFR:FE001ADE] (places _ArtCommonBench "Bench" [FURN:FE00199C] in GRUP Cell Temporary Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: [REFR:FE001A14] (places _ArtWoodenWritingChair "Chair" [FURN:FE001A15] in GRUP Cell Temporary Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: [REFR:0010E398] (places SpitPotClosedLoose01 "Pot" [CONT:00054174] in GRUP Cell Temporary Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: UnusedOrigCandleLightNS [REFR:000DA559] (places DefaultCandleLight01NSDesat [LIGH:00088241] in GRUP Cell Temporary Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: [REFR:000CADED] (places DB01AventusLetter "Letter from the Steward" [BOOK:000CADEC] in GRUP Cell Temporary Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: [REFR:000C7210] (places TreasKnapsack "Knapsack" [CONT:000B7879] in GRUP Cell Temporary Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: [REFR:000C7205] (places EndTable01 "End Table" [CONT:00024CA6] in GRUP Cell Temporary Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: [REFR:000C71F2] (places Dresser01 "Dresser" [CONT:00024CA5] in GRUP Cell Temporary Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: UnusedOrigCandleLight [REFR:0007D0B8] (places DefaultCandleLight01_NoMove [LIGH:0005C677] in GRUP Cell Temporary Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: [REFR:0007D057] (places WHIntWoodFloorMid02 [STAT:0007BA6B] in GRUP Cell Temporary Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: [REFR:0007D047] (places WHIntStairs01 [STAT:0007D046] in GRUP Cell Temporary Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: _ArtSupperChild2Veg [REFR:FE001A5F] (places FoodLeeksGrilled "Grilled Leeks" [ALCH:00064B3E] in GRUP Cell Persistent Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: _ArtSupperChild1Veg [REFR:FE001A5E] (places FoodLeeksGrilled "Grilled Leeks" [ALCH:00064B3E] in GRUP Cell Persistent Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: _ArtSupperSnack [REFR:FE001A5D] (places FoodApple "Red Apple" [ALCH:00064B2E] in GRUP Cell Persistent Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: _ArtSupperDesert [REFR:FE001A5C] (places FoodHoneyNutTreat "Honey Nut Treat" [ALCH:00064B38] in GRUP Cell Persistent Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: _ArtSupperAngrVeg [REFR:FE001A5B] (places FoodLeeksGrilled "Grilled Leeks" [ALCH:00064B3E] in GRUP Cell Persistent Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: _ArtSupperChild2Fish [REFR:FE001A5A] (places FoodSlaughterfishCooked "Seared Slaughterfish" [ALCH:00064B3C] in GRUP Cell Persistent Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: _ArtSupperChild1Fish [REFR:FE001A59] (places FoodSlaughterfishCooked "Seared Slaughterfish" [ALCH:00064B3C] in GRUP Cell Persistent Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: _ArtSupperAngrFish [REFR:FE001A58] (places FoodSlaughterfishCooked "Seared Slaughterfish" [ALCH:00064B3C] in GRUP Cell Persistent Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: _ArtSupperAngrBread [REFR:FE001A57] (places FoodBread01B "Bread" [ALCH:00065C98] in GRUP Cell Persistent Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: _ArtSupperChild2Bread [REFR:FE001A56] (places FoodBread01B "Bread" [ALCH:00065C98] in GRUP Cell Persistent Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: _ArtSupperChild1Bread [REFR:FE001A55] (places FoodBread01B "Bread" [ALCH:00065C98] in GRUP Cell Persistent Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: _ArtSupperAngrPlate [REFR:FE001A54] (places BasicPlate02 "Plate" [MISC:000E2618] in GRUP Cell Persistent Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: _ArtSupperChild1Plate [REFR:FE001A53] (places BasicWoodenPlate01 "Wooden Plate" [MISC:00031941] in GRUP Cell Persistent Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: _ArtSupperChild2Plate [REFR:FE001A52] (places BasicWoodenPlate01 "Wooden Plate" [MISC:00031941] in GRUP Cell Persistent Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: _ArtChildAventusMarker [REFR:FE001A3C] (places XMarker [STAT:0000003B] in GRUP Cell Persistent Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: _ArtChildSofieMarker [REFR:FE001A3B] (places XMarker [STAT:0000003B] in GRUP Cell Persistent Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: _ArtSitCasual2 [REFR:FE0019AA] (places InvisibleChairMarkerChild "Invisible Chair Marker" [FURN:000A035C] in GRUP Cell Persistent Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: _ArtSitCasual [REFR:FE0019A9] (places InvisibleChairMarkerFChild "Invisible Chair Marker" [FURN:00017041] in GRUP Cell Persistent Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: _ArtSpareRoomJunkMarker [REFR:FE00190A] (places XMarker [STAT:0000003B] in GRUP Cell Persistent Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: _ArtArmoryMarker [REFR:FE0018B1] (places XMarker [STAT:0000003B] in GRUP Cell Persistent Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: _ArtLibraryMarker [REFR:FE0018B0] (places XMarker [STAT:0000003B] in GRUP Cell Persistent Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: _ArtEntryMarker [REFR:FE0018A6] (places XMarker [STAT:0000003B] in GRUP Cell Persistent Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: _ArtLoftBedroomMarker [REFR:FE001879] (places XMarker [STAT:0000003B] in GRUP Cell Persistent Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: _ArtFollowerRoomMarker [REFR:FE001878] (places XMarker [STAT:0000003B] in GRUP Cell Persistent Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: _ArtChildRoomMarker [REFR:FE001877] (places XMarker [STAT:0000003B] in GRUP Cell Persistent Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: _ArtLivingMarker [REFR:FE001876] (places XMarker [STAT:0000003B] in GRUP Cell Persistent Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: _ArtKitchenMarker [REFR:FE001875] (places XMarker [STAT:0000003B] in GRUP Cell Persistent Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: _ArtDiningMarker [REFR:FE001874] (places XMarker [STAT:0000003B] in GRUP Cell Persistent Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: _ArtAlchemyMarker [REFR:FE001873] (places XMarker [STAT:0000003B] in GRUP Cell Persistent Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: _ArtEnchantingMarker [REFR:FE001872] (places XMarker [STAT:0000003B] in GRUP Cell Persistent Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently disabling: _ArtAngrenorRoomMarker [REFR:FE001871] (places XMarker [STAT:0000003B] in GRUP Cell Persistent Children of WindhelmAretinoResidence "Aretino Residence" [CELL:00016784])
Permanently Disabled Records: 44
[00:00] Done: Applying script "Permanently Disable Initially Disabled References", Processed Records: 1094, Elapsed Time: 00:00

The xEdit script seems to work fine for me but YMMV. USE AT YOUR OWN RISK.

Link to comment
Share on other sites

  • 0
42 minutes ago, Mousetick said:

The xEdit script seems to work fine for me but YMMV. USE AT YOUR OWN RISK.

:thumbsup:

Is this good? I'm shite at scripting, so please check:

Spoiler

unit UserScript;

var
  UndeletedCount, NAVMCount: integer;
  
function Process(e: IInterface): integer;
var
  Sig: string;
  xesp: IInterface;
begin
  Result := 0;
  
  if not (IsEditable(e) and GetIsDeleted(e)) then
    Exit;
  
  Sig := Signature(e);

  if 
    (Sig <> 'REFR') and
    (Sig <> 'PGRE') and
    (Sig <> 'PMIS') and
    (Sig <> 'ACHR') and
    (Sig <> 'ACRE') and
    (Sig <> 'NAVM') and
    (Sig <> 'PARW') and // Skyrim
    (Sig <> 'PBAR') and // Skyrim
    (Sig <> 'PBEA') and // Skyrim
    (Sig <> 'PCON') and // Skyrim
    (Sig <> 'PFLA') and // Skyrim
    (Sig <> 'PHZD')     // Skyrim
  then
    Exit;

  if not (IsEditable(e) and GetIsInitiallyDisabled(e) then

    Exit;
  end;
  
  AddMessage('Permanently disabling: ' + Name(e));

  // set persistence flag depending on game
  if (wbGameMode = gmFO3) or (wbGameMode = gmFNV) or (wbGameMode = gmTES5) and ((Sig = 'ACHR') or (Sig = 'ACRE')) then
    SetIsPersistent(e, True)
  else if wbGameMode = gmTES4 then
    SetIsPersistent(e, False);
    
  // place it below the ground
  if not GetIsPersistent(e) then
    SetElementNativeValues(e, 'DATA\Position\Z', -30000);

  RemoveElement(e, 'Enable Parent');
  RemoveElement(e, 'XTEL');
  // ... remove anything else here
  
  // set to disabled
  SetIsInitiallyDisabled(e, True);
  
  // add enabled opposite of player (true - silent)
  xesp := Add(e, 'XESP', True);
  if Assigned(xesp) then begin
    SetElementNativeValues(xesp, 'Reference', $14); // Player ref
    SetElementNativeValues(xesp, 'Flags', 1);  // opposite of parent flag
  end;

  Inc(UndeletedCount);
end;

function Finalize: integer;
begin
  AddMessage('Permanently Disabled Records: ' + IntToStr(UndeletedCount));

end;

end.

 

3 minutes ago, Mousetick said:

Slightly better and safer version which skips Initially Disabled references if they already have an Enable Parent:

image.png

As discussed above, the script doesn't check if the reference might be used any other way by the plugin, which would require cross-referencing the reference in the plugin. I'm not willing to spend more than 10 minutes on this, so this is the best you'll get.

Oh wait, I have to do it all over again, hold on

Link to comment
Share on other sites

  • 0
6 minutes ago, Mousetick said:

Slightly better and safer version which skips Initially Disabled references if they already have an Enable Parent:

image.png

As discussed above, the script doesn't check if the reference might be used any other way by the plugin, which would require cross-referencing the reference in the plugin. I'm not willing to spend more than 10 minutes on this, so this is the best you'll get.

Is this good?

Spoiler

unit UserScript;

var
  UndeletedCount, NAVMCount: integer;
  
function Process(e: IInterface): integer;
var
  Sig: string;
  xesp: IInterface;
begin
  Result := 0;
  
  if not (IsEditable(e) and GetIsDeleted(e)) then
    Exit;
  
  Sig := Signature(e);

  if 
    (Sig <> 'REFR') and
    (Sig <> 'PGRE') and
    (Sig <> 'PMIS') and
    (Sig <> 'ACHR') and
    (Sig <> 'ACRE') and
    (Sig <> 'PARW') and // Skyrim
    (Sig <> 'PBAR') and // Skyrim
    (Sig <> 'PBEA') and // Skyrim
    (Sig <> 'PCON') and // Skyrim
    (Sig <> 'PFLA') and // Skyrim
    (Sig <> 'PHZD')     // Skyrim
  then
    Exit;

  if not (IsEditable(e) and GetIsInitiallyDisabled(e) and not ElementExists(e, 'XESP')) then

    Exit;
  
  AddMessage('Permanently disabling: ' + Name(e));

  // set persistence flag depending on game
  if (wbGameMode = gmFO3) or (wbGameMode = gmFNV) or (wbGameMode = gmTES5) and ((Sig = 'ACHR') or (Sig = 'ACRE')) then
    SetIsPersistent(e, True)
  else if wbGameMode = gmTES4 then
    SetIsPersistent(e, False);
    
  // place it below the ground
  if not GetIsPersistent(e) then
    SetElementNativeValues(e, 'DATA\Position\Z', -30000);

  RemoveElement(e, 'Enable Parent');
  RemoveElement(e, 'XTEL');
  // ... remove anything else here
  
  // set to disabled
  SetIsInitiallyDisabled(e, True);
  
  // add enabled opposite of player (true - silent)
  xesp := Add(e, 'XESP', True);
  if Assigned(xesp) then begin
    SetElementNativeValues(xesp, 'Reference', $14); // Player ref
    SetElementNativeValues(xesp, 'Flags', 1);  // opposite of parent flag
  end;

  Inc(UndeletedCount);
end;

function Finalize: integer;
begin
  AddMessage('Permanently Disabled Records: ' + IntToStr(UndeletedCount));

end;

end.

 

Link to comment
Share on other sites

  • 0

You don't need to be good at scripting, simply edit with a text editor to match the right side of the comparison. Looks like you missed some pieces or put them in the wrong place, though.

Edited by Mousetick
Removed downloadable attachment
  • Thanks 1
Link to comment
Share on other sites

  • 0

@Mousetick Me again... I have another query, if you can help.

So this whole process - can it be done in reverse? In other words, a script that finds Initially Disabled and removes that flag, as well as XESP (if there is one), and also returns Z axis to the master value (if it's set to -30000). There are some placed objects (trees) that I don't want to disable, but I want to keep their modified Scale (XSCL).

Link to comment
Share on other sites

  • 0
14 minutes ago, Katarsi said:

So this whole process - can it be done in reverse? In other words, a script that finds Initially Disabled and removes that flag, as well as XESP (if there is one), and also returns Z axis to the master value (if it's set to -30000).

Now you want to undo the whole thing? That doesn't make much sense.

17 minutes ago, Katarsi said:

There are some placed objects (trees) that I don't want to disable, but I want to keep their modified Scale (XSCL).

Restore the original plugin from backup. Manually remove the 'Initially Disabled' flag from the trees you want to keep first. Then run the script you originally asked for to permanently disable everything else that is Initially Disabled and doesn't have an Enable Parent.

Link to comment
Share on other sites

  • 0
1 hour ago, Katarsi said:

It's not for the same mod. And this one has a lot of Initially Disabled that I want to actually enable.

This couldn't be automated/scripted. The script wouldn't know which ones you want to keep/enable based on your personal taste. It's all or nothing based on objective criteria.

I'd tackle this manually like this:

  • Create an empty plugin. This is going to be a patch for the mod.
  • Sort the patch after the mod.
  • Load both the mod plugin and the patch in xEdit.
  • For each disabled tree that you want to keep:
    • Copy as Override... from Skyrim.esm (NOT from the mod plugin), or whichever is the last vanilla plugin, into the patch.
    • Drag the scale and any other change you like from the mod plugin to the patch.

This should be faster than un-disabling the trees one by one. Also it's better to put manual edits into a separate patch than editing the mod plugin directly. In case the mod is updated or reinstalled, nothing is lost.

* The Copy as Override can be done in small batches, rather than one by one, by selecting multiple records first (shift-click or ctrl-click).

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...

Important Information

By using this site, you agree to our Guidelines, Privacy Policy, and Terms of Use.