Jump to content

[WIPz] TES5Edit


zilav
 Share

Recommended Posts

I don't see why a fake record is a problem. It's only used in the DAT files, which are not used by the game, CK, or any other plugin. (The PLYR group will need to be disabled in the Add popup menu, but I don't know how to do that.) It's just a means for xEdit to display and work with $14 in a way that does not interfere with other functionality, and in that sense, is far superior to the string-based approach. It also doesn't introduce conflicts as the hardcoded cell approach would. Looks like a win-win solution to me.

Link to comment
Share on other sites

8 minutes ago, fireundubh said:

I don't see why a fake record is a problem. It's only used in the DAT files, which are not used by the game, CK, or any other plugin. (The PLYR group will need to be disabled in the Add popup menu, but I don't know how to do that.) It's just a means for xEdit to display and work with $14 in a way that does not interfere with other functionality, and in that sense, is far superior to the string-based approach.

It will conflict in xEdit, not in the game and CK. Hardcoded forms in xEdit does not interfere with anything.

Also what next? Hardcode a fake NULL record just to see where it is used? Hardcode FFFF record which I added for Fallout 4 because Bethesda decided that zero FormID is not enough and they also need to have a negative FormID = -1? Hardcode more possible FormIDs as records in future games?

There are some hardcoded FormID numbers, not records, that have a special meaning for the game, and xEdit treats them just like that. I don't see any problems with the current implementation. If you really want to see where those hardcoded FormID numbers are used, a simple custom filtering xEdit script is enough for that rare task. In fact you are the only person ever asking to see references of hardcoded forms in 6 years.

I'm not against fixing issues, but I just don't see any issue here in the first place to begin with.

Link to comment
Share on other sites

2 hours ago, zilav said:

It will conflict in xEdit, not in the game and CK. Hardcoded forms in xEdit does not interfere with anything.

Conflict with what!? What could possibly conflict with 00000014?
 

2 hours ago, zilav said:

I don't see any problems with the current implementation.

No problems with the current implementation? I already listed them. A fake record for the Player serves the same exact purpose that the string approach does but without the drawbacks.

The fake record solution causes zero issues, AFAIK, and corrects a hack that interferes with scripts and other xEdit features.

I really don't understand your resistance here. You don't even have to do anything. I've already done the work.
 

2 hours ago, zilav said:

In fact you are the only person ever asking to see references of hardcoded forms in 6 years.

Maybe because there's only three people who write complex xEdit scripts: you, mator, and me.

But I also remember a conversation once about seeing Player references, which is not really my goal here but remains a good side effect.

I don't think I should have to use a Regular Expression to get the Editor ID or Signature of a CTDA reference, or use a TStringList as a dictionary, or any number of alternative ways to look up and compare data just because I have to work around the special handling of $14.

Compare:

// function RegExReplace(const ptrn, repl, subj: String): String;
// var
// 	re: TPerlRegEx;
// 	output: String;
// begin
// 	re := TPerlRegEx.Create;
// 	try
// 		re.RegEx := ptrn;
// 		re.Options := [];
// 		re.Subject := subj;
// 		re.Replacement := repl;
// 		re.ReplaceAll;
// 		output := re.Subject;
// 	finally
// 		re.Free;
// 		Result := output;
// 	end;
// end;

runsOn := Trim(RegExReplace('\[(.*)\]$', '', GetEditValue(ctdaRef)));

With:

runsOn := EditorID(LinksTo(ctdaRef));

The latter does not work because the strings-based approach does not return an Editor ID for the Player. You could then hack EditorID to return the right string for the Player. But then you'd be hacking in special handling for other functions, too, and it just gets out-of-hand trying to handle $14 differently than every other record. You also have the issue of a string masquerading as a record, so functions like LinksTo don't throw exceptions when the "record" is "Player [00000014]" and just return nothing, which makes debugging harder than necessary.

You have a system in place for hardcoded forms, which you're already using in spades for every game. Why is adding Player really so bad?

Link to comment
Share on other sites

12 hours ago, fireundubh said:

Conflict with what!? What could possibly conflict with 00000014?

The newly introduced fake non-existent CELL can conflict because it will require some FormID. As I said 00000014 is ACHR placing NPC 0000007, not a PLYR record which is simply a made up xEdit's abbreviation similar to NULL - Null Reference.

And you confirmed that this questionable change is to make only your life easier so you don't need to add additional check for the player ref in your own scripts. The game engine does this check, CK does this check, xEdit does this check, but only you refuse to because it is somehow a "hack"

if GetNativeValue(ctdaref) = $14 then
	runsOn := 'Player'
else
	runsOn := EditorID(LinksTo(ctdaRef));

which you can wrap into a custom function and call everywhere to get the EditorID of a reference and just forget about the PLYR's existence.

Well what you propose is the definition of a hack because xEdit right now does this the conventional way like the game and official editor.

Elminster went this way for a reason when implementing the Player instance when he could simply add it into hardcoded forms file. And right now I don't see any reason to change that apart from making life a bit easier for 3 people which is not enough. Make that 2 actually because I don't care, searched through all of my hundreds of scripts from the past 6 years and I never had to deal with the Player instance, this tells how important that thing is in general.

Link to comment
Share on other sites

What fake CELL? That was your own idea that you ruled out because there could be conflicts.

My solution is a fake PLYR record that behaves in much the same way as the fake PLYR signature. We have .dat files that provide us a way of handling addresses that aren't in the ESMs. Why are we not using those .dat files to handle $14?

Both the fake PLYR record and the fake PLYR signature are hacks, yes, but from xEdit's perspective, treating $14 as a string in a program that revolves around records is also a hack. That inconsistency has fallout: all other code that involves records has to account for $14 not being a record. When Elminster implemented $14 handling, there were no user scripts and, I think, the Referenced By feature also did not exist. Handling that inconsistency had some internal implications but that was it.

However, we have user scripts and a Referenced By tab now - and thus a collection of external functions that return nothing or that don't throw exceptions when $14 is passed in, and a feature that can't be used to show references to $14.

To me, you deal with this in one of two ways:

  • implement a fake PLYR record, which fills in all the gaps without any drawbacks whatsoever; or
  • implement further special handling of $14 in those functions (returning the right data or throwing exceptions) and somehow implement a way to show which records reference $14.

One of those solutions is ready to be implemented right now through a simple pull request - and you're choosing "do nothing." :facepalm:

Also, yes, technically, NULL should be a record because "NONE" is the actual signature of NULL references, so indeed, NONE would not be a fake record.

Link to comment
Share on other sites

3 hours ago, fireundubh said:

What fake CELL? That was your own idea that you ruled out because there could be conflicts.

You can't have ACHR without a parent CELL in plugin. As I said I'm against fake made up form types, the only thing I would settle on is adding the real ACHR, nothing else. CK substitues zero with "NONE" and $14 with "PlayerRef" iirc, xEdit does exactly the same just using own labels for internal reasons (of how definitions are set up) and yet you consider this a hack somehow. I choose to "do nothing" because I'm against this, and I mentioned several times why. You just don't listen and insist on implementing your hack.

What do you mean under "implement further special handling of $14"? I've never heard of anyone complaining about xEdit causing issues because of using this ref. You are the first one to mention it.

Link to comment
Share on other sites

1 hour ago, zilav said:

What do you mean under "implement further special handling of $14"? I've never heard of anyone complaining about xEdit causing issues because of using this ref. You are the first one to mention it.

If you run EditorID on a field that contains the string "Player [00000014]," you'll get an empty string.

In that case, further special handling would mean checking whether $14 was passed into EditorID, and either returning the string "Player" from EditorID, or throwing an exception.

For example:

procedure IwbMainRecord_EditorID(var Value: Variant; Args: TJvInterpreterArgs);
var
  MainRecord: IwbMainRecord;
begin
  Value := '';
  if Supports(IInterface(Args.Values[0]), IwbMainRecord, MainRecord) then
    Value := StrUtils.IfThen(MainRecord.NativeValue = $14, 'Player', MainRecord.EditorID);
end;

The rest of the scripting functions that take an IInterface parameter (e.g., Signature, LinksTo) would also need similar treatment.

Link to comment
Share on other sites

13 hours ago, fireundubh said:

The rest of the scripting functions that take an IInterface parameter (e.g., Signature, LinksTo) would also need similar treatment.

EditorID(aRecord: IwbMainRecord) requires a record as input parameter. Since $14 like I mentioned is not a real record but hardcoded FormID number, you can't use this function because there is nothing to pass as the input.

Signatute(aRecord: IwbMainRecord) is the same, requires a record.

LinksTo() returns a referenced record, which again doesn't exist for $14.

All those functions work as intended. The only function that has to account for hardcoded FormID numbers is Check() to not report false positives for references pointing to hardcoded FormIDs, and it already does that correctly. What else "doesn't work"?

You still don't grasp the difference between a simpe FormID number and a record with FormID. That player reference ACHR record using FormID = $14 exists only at runtime, but it doesn't exist otherwise in plugins when using official editor, xEdit, snip or any other software which can deal with plugin data format. You work with plugins not in runtime. But if you are going to create a script extender dll plugin then yes, you will have access to this PlayerRef form by it's memory address (I believe it exists, but not checked personally though) because you are in runtime.

Link to comment
Share on other sites

I know the difference between a FormID string and a record with a FormID. That's the problem: they're different and they're treated differently. LOL.
 

Run this script on a TERM record with a VMAD subrecord that has two or more object properties, one of which contains the string "Player [00000014]" in the FormID field.

unit UserScript;

const 
	PropertyRefPath = 'Value\Object Union\Object v2\FormID';

function Process(e: IInterface): Integer;
var
	i: Integer;
	properties, p, ref: IInterface;
begin
	properties := ElementByPath(e, 'VMAD\Script Fragments\Script\Properties');
	for i := 0 to Pred(ElementCount(properties)) do begin
		p := ElementByIndex(properties, i);
		ref := LinksTo(ElementByPath(p, PropertyRefPath));
		AddMessage(EditorID(ref));		
	end;
end;

end.

That script will return results similar to:

Applying script...
AutoLoot_Perks

[Apply Script done]  Processed Records: 1, Elapsed Time: 00:00

Change EditorID to Signature and:

Applying script...
FLST

[Apply Script done]  Processed Records: 1, Elapsed Time: 00:00

This is bad. Either EditorID and Signature need to return values, or they need to raise exceptions. Right now, they return zero-length strings.

You can't even use try-except here because the zero-length string is a valid result.
 

So, instead of the above loop, you have to do something wonky create a unique EditorID function that takes two parameters:

unit UserScript;

const 
	PropertyRefPath = 'Value\Object Union\Object v2\FormID';

function GetEditorID(mainRec, linkedRec: IInterface): String;
begin
	Result := IfThen(GetNativeValue(mainRec) = $14, 'Player', EditorID(linkedRec));
end;

function Process(e: IInterface): Integer;
var
	i: Integer;
	properties, p, ref: IInterface;
begin
	properties := ElementByPath(e, 'VMAD\Script Fragments\Script\Properties');
	for i := 0 to Pred(ElementCount(properties)) do begin
		p := ElementByIndex(properties, i);
		ref := ElementByPath(p, PropertyRefPath);
		AddMessage(GetEditorID(ref, LinksTo(ref)));		
	end;
end;

end.

 

But what if you want to return a linked element from the loop? Well, if you want to also handle the Player, you can't do that directly. You have to do something like this:

unit UserScript;

const 
	MatchingID = 'Player';
	PropertyRefPath = 'Value\Object Union\Object v2\FormID';

function GetEditorID(mainRec, linkedRec: IInterface): String;
begin
	Result := IfThen(GetNativeValue(mainRec) = $14, 'Player', EditorID(linkedRec));
end;

function GetMatchingProperty(properties: IInterface; MatchingID: String): IInterface;
var
	i: Integer;
	p, ref, linkedRef: IInterface;
begin
	for i := 0 to Pred(ElementCount(properties)) do begin
		p := ElementByIndex(properties, i);
		ref := ElementByPath(p, PropertyRefPath);
		linkedRef := LinksTo(ref);

		if GetEditorID(ref, linkedRef) = MatchingID then
			break;		
	end;

	if GetEditorID(ref, linkedRef) <> MatchingID then
		raise Exception.Create('Could not find matching property');

	Result := ref;
end;

function Process(e: IInterface): Integer;
var
	properties, ref: IInterface;
begin
	properties := ElementByPath(e, 'VMAD\Script Fragments\Script\Properties');
	ref := GetMatchingProperty(properties, MatchingID);
	AddMessage(GetEditorID(ref, LinksTo(ref)));
end;

end.

 

If you didn't need to handle the Player differently, this script would be fine:

unit UserScript;

const
	MatchingId = 'AutoLoot_Perks';
	PropertyRefPath = 'Value\Object Union\Object v2\FormID';

function Process(e: IInterface): Integer;
var
	i: Integer;
	properties, p, ref: IInterface;
	edid: String;
begin
	properties := ElementByPath(e, 'VMAD\Script Fragments\Script\Properties');
	for i := 0 to Pred(ElementCount(properties)) do begin
		p := ElementByIndex(properties, i);
		ref := LinksTo(ElementByPath(p, PropertyRefPath));
		edid := EditorID(ref);

		if edid = MatchingId then
			break;	
	end;

	if edid <> MatchingId then
		raise Exception.Create('Could not find matching property');

	// do something with the linked ref or edid
	AddMessage(Signature(ref));
end;

end.

 

So, I hope where words have failed, the code shows you what I'm talking about.

In theory, EditorID, Signature, and LinksTo require an IInterface parameter, but the internal handling of $14 tricks these functions into treating a string as a record...

...as a record with no data, hence the fake record in my original post about this issue.

 

TLDR:

  • You can pass in a string to functions that require an IInterface parameter via LinksTo if that string is the $14 non-record record in a FormID field.
  • Those functions that return a string will return a zero-length string.
Link to comment
Share on other sites

When you need to pass around a referenced object (or some record in general), use it's string representation GetElementEditValues(p, PropertyPath). Unless you want to access data in the linked record, there is no point in treating it like IInterface. xEdit is built around dealing with edit values - what you actually see and change in GUI, even internal copying of records are done via edit values, not using native binary data. Why do you want to access in the linked record itself in this particular case that requires you to resolve it with LinksTo()? Editor ID is already covered by a simple check for $14, what else?

Link to comment
Share on other sites

2 hours ago, zilav said:

Unless you want to access data in the linked record, there is no point in treating it like IInterface. [...] Why do you want to access in the linked record itself in this particular case that requires you to resolve it with LinksTo()? Editor ID is already covered by a simple check for $14, what else?

To use the FormID, EditorID, Signature, and other functions, you need to use the LinksTo function. Fact. The examples above are just examples to illustrate the issue.

Link to comment
Share on other sites

21 hours ago, fireundubh said:

To use the FormID, EditorID, Signature, and other functions, you need to use the LinksTo function. Fact. The examples above are just examples to illustrate the issue.

You simply need an existing record, not "use the LinksTo". When records don't exist (either because they exist only at runtime or a ref points to invalid record), you don't call LinksTo() on such refs, you need to perform additional check. How do you handle broken refs? LinksTo() will return nil, and calling EditorID() without a prior check will fail too.

The same for all functions which require a record as an input parameter. The Check() function for example don't require a record and hence it works fine with hardcoded FormIDs right now. That's what I'm asking - what other functions which don't require a record don't work right now with hardcoded FormIDs? Because that must be fixed.

Link to comment
Share on other sites

1 hour ago, zilav said:

You simply need an existing record, not "use the LinksTo". When records don't exist (either because they exist only at runtime or a ref points to invalid record), you don't call LinksTo() on such refs, you need to perform additional check. How do you handle broken refs? LinksTo() will return nil, and calling EditorID() without a prior check will fail too.

In the project I'm working on, I just opted to avoid using FormID, Signature, EditorID, and LinksTo in favor of RegEx. I think it might even be faster, but I don't know if that's because I replaced looping over elements with preloading script properties and menu items into "dictionaries." (Too bad we can't use TDictionary, but SplitString with TStringDynArray seems to be okay.)
 

1 hour ago, zilav said:

The same for all functions which require a record as an input parameter. The Check() function for example don't require a record and hence it works fine with hardcoded FormIDs right now. That's what I'm asking - what other functions which don't require a record don't work right now with hardcoded FormIDs? Because that must be fixed.

I don't know. Which functions other than Check don't require a record? I can write some unit tests after I finish up my project.

Link to comment
Share on other sites

Happy Holidays Everybody!!!!!  :santa:

I am working on a new script and could use some input. 

What this script will do is put a Package on almost every NPC in the WhiterunCrimeFaction.

I will add all of the other holds once I get it working.

This Package will need to be placed in the Top Position so it has priority over all other packages. 

This is where I am hoping to get some suggestions.   I will need to add the package and then arrange the Packages on the NPC without changing their existing order or removing any of them. 

Link to comment
Share on other sites

7 hours ago, Dragonblood said:

What this script will do is put a Package on almost every NPC in the WhiterunCrimeFaction.

unit AddTopPackageToNPC;

//===============================================================================
// check if NPC is in faction (by provided Editor ID of a faction)
function IsInFaction(npc: IInterface; aFaction: string): boolean;
var
  entries: IInterface;
  i: integer;
begin
  entries := ElementByName(npc, 'Factions');
  for i := 0 to Pred(ElementCount(entries)) do
    if EditorID( LinksTo( ElementByName( ElementByIndex(entries, i), 'Faction' ) ) ) = aFaction then begin
      Result := True;
      Exit;
    end;
end;

//===============================================================================
// add top element to unsorted list aListName in a record, create if missing
function AddTopListElement(r: IInterface; aListName: string): IInterface;
var
  entries: IInterface;
  i: integer;
begin
  entries := ElementByName(r, aListName);
  
  // if list exist
  if Assigned(entries) then begin
    // append a new entry
    Result := ElementAssign(entries, HighInteger, nil, False);
    // move it to the top of the list
    for i := 1 to Pred(ElementCount(entries)) do
      MoveUp(Result);
  end
  
  // otherwise create new list and get the first entry which is added automatically
  else
    Result := ElementByIndex(Add(r, aListName, True), 0);
end;


function Process(e: IInterface): integer;
begin
  if Signature(e) <> 'NPC_' then
    Exit;

  if not IsInFaction(e, 'WhiterunCrimeFaction') then
    Exit;
  
  AddMessage('Adding package to ' + Name(e));
  SetEditValue(AddTopListElement(e, 'Packages'), 'DefaultSandboxEditorLocation512 [PACK:0001B217]');
  
end;

end.

 

Link to comment
Share on other sites

I thought I was going to be able to pull it off this time.  Nope.   The NPCs are added to the plugin but it will not add the Package.  I can't see what the problem is.  

No Errors.

unit AddTopPackageToNPC;

var
  plugin: IInterface;

//===============================================================================
// check if NPC is in faction (by provided Editor ID of a faction)
function IsInFaction(npc: IInterface; aFaction: string): boolean;
var
  entries: IInterface;
  i: integer;
begin
  entries := ElementByName(npc, 'Factions');
  for i := 0 to Pred(ElementCount(entries)) do
    if EditorID( LinksTo( ElementByName( ElementByIndex(entries, i), 'Faction' ) ) ) = 'CrimeFactionWhiterun' then begin
      Result := True;
      Exit;
    end;
end;

//===============================================================================
// add top element to unsorted list aListName in a record, create if missing
function AddTopListElement(r: IInterface; aListName: string): IInterface;
var
  entries: IInterface;
  i: integer;
begin
  entries := ElementByName(r, 'aListName');
  
  // if list exist
  if Assigned(entries) then begin
    // append a new entry
    Result := ElementAssign(entries, HighInteger, nil, False);
    // move it to the top of the list
    for i := 1 to Pred(ElementCount(entries)) do
      MoveUp(Result);
  end
  
  // otherwise create new list and get the first entry which is added automatically
  else
    Result := ElementByIndex(Add(r, 'aListName', True), 0);
end;
//===============================================================================

function Process(e: IInterface): integer;
var
 r: IInterface;
begin

  if Signature(e) <> 'NPC_' then
    Exit;

  if not IsInFaction(e, 'CrimeFactionWhiterun') then
    Exit;
  
  if not Assigned(plugin) then begin
    if MessageDlg('Create a new plugin [YES] or append to the last loaded one [NO]?', mtConfirmation, [mbYes, mbNo], 0) = mrYes then
      plugin := AddNewFile
    else
      plugin := FileByIndex(Pred(FileCount));
    if not Assigned(plugin) then begin
      Result := 1;
      Exit;
    end;
  end;  

  // and NPC itself
  AddRequiredElementMasters(e, plugin, False);

  try
    // copy NPC as override
    r := wbCopyElementToFile(e, plugin, False, True);
    
    // Add Package to Top of list.
    AddMessage('Adding package to ' + Name(r));
    SetEditValue(AddTopListElement(r, 'Packages'), 'DA15StayAtEditorLocationBully [PACK:00000E40]');
  except
    on Ex: Exception do begin
      AddMessage('Failed to copy: ' + FullPath(e));
      AddMessage('    reason: ' + Ex.Message);
    end;
  end;
end;


function Finalize: integer;
begin
  if Assigned(plugin) then
    SortMasters(plugin);
end;

end.

 

Link to comment
Share on other sites

8 hours ago, Dragonblood said:

I thought I was going to be able to pull it off this time.  Nope.   The NPCs are added to the plugin but it will not add the Package.  I can't see what the problem is. 

You need to provide the full formid record string as I shown in my example instead of just 'MacPack'. You also don't need this

AddRequiredElementMasters(LinksTo(ElementByIndex(e, 0)), plugin, False);

because NPC records don't have a parent record.

Link to comment
Share on other sites

2 hours ago, zilav said:

You need to provide the full formid record string as I shown in my example instead of just 'MacPack'. You also don't need this

because NPC records don't have a parent record.

Done,  Unfortunately same result.

Something else that I can not understand.  This function will not work unless I put the faction in the Function.  I tried this will the Package also but it did not make a difference.

if not IsInFaction(e, 'CrimeWhiterunFaction') then
    Exit;   
if EditorID( LinksTo( ElementByName( ElementByIndex(entries, i), 'Faction' ) ) ) = 'aFaction' then begin  //  Can not use aFaction must use 'CrimeWhiterunFaction'

I have updated the script in the post above.  I am using a Vanilla Package until I can get the script to add a one. 

BTW  MAC Stands for More Assertive Citizens. 

 

I have attempted to simply add the Package to the NPC but I can not get it to work.   Can we just do that first?   No Function, just add the Package to the NPC.  Then I can work on Moving it. 

Thanks!!

 

Link to comment
Share on other sites

3 hours ago, Dragonblood said:

Done,  Unfortunately same result.

Sorry, my bad. Remove quotes from this line to look like this

entries := ElementByName(r, aListName);

Initially there was 'Packages', but then I replaced it with a function parameter to make function universal (adding top entry to any kinds of unsorted lists) when posting here but forgot about quotes, so it was literally searching for 'aListName' element in NPC record :D

Link to comment
Share on other sites

2 hours ago, zilav said:

Sorry, my bad. Remove quotes from this line to look like this.

No Problem.  It works perfectly now.  :clap:  

I learned quite a bit also.  This script was much different than the other script.  Different functions.

This appears to never return 1 I must be doing something wrong.  But I do not know how to get Use AI Packages without using the Path.

if GetElementNativeValues(e, 'ACBS\Template Flags\Use AI Packages') <> 0 then
Exit;

Thanks.

Link to comment
Share on other sites

4 hours ago, Dragonblood said:

This appears to never return 1 I must be doing something wrong.  But I do not know how to get Use AI Packages without using the Path.

if GetElementNativeValues(e, 'ACBS\Template Flags\Use AI Packages') <> 0 then
Exit;

Thanks.

No, this works fine, tested myself. It will exit if flag is set. Something else must be wrong if it doesn't work for you, or you applied script to the wrong NPCs.

Link to comment
Share on other sites

4 hours ago, zilav said:

No, this works fine, tested myself. It will exit if flag is set. Something else must be wrong if it doesn't work for you, or you applied script to the wrong NPCs.

Thanks again for your help.  When I tested it on the Whiterun Crime Faction it appeared to work.  There were 0 NPCs with that flag set.  So to be sure I ran another test. 

I removed the script that was checking for the faction and replaced the 0 with a 1.

if Signature(e) <> 'NPC_' then
Exit;
 
if GetElementNativeValues(e,'ACBS\Template Flags\Use AI Packages') <> 1 then
Exit;

I ran this on the all of the NPCs in the Skyrim.esm  and it exits every time.  There should be hundreds of NPCs with that flag set.

Link to comment
Share on other sites

42 minutes ago, Dragonblood said:

I ran this on the all of the NPCs in the Skyrim.esm  and it exits every time.  There should be dozens of NPCs with that flag set.

I ran it too and it patched 147 NPCs from Skyrim.esm with both flag and faction check https://pastebin.com/raw/qM1ZqgCS

No idea what's wrong in your case. Maybe you've made some other changes in the script which caused it to fail.

Link to comment
Share on other sites

20 hours ago, zilav said:

I ran it too and it patched 147 NPCs from Skyrim.esm with both flag and faction check https://pastebin.com/raw/qM1ZqgCS

No idea what's wrong in your case. Maybe you've made some other changes in the script which caused it to fail.

No Problem.  Using the 1 does not work.  It exits every time.  

if GetElementNativeValues(e,'ACBS\Template Flags\Use AI Packages') <> 1 then
Exit;
 
However this works perfectly to get all of the NPCs with the Flag Set. 
if not  GetElementNativeValues(e,'ACBS\Template Flags\Use AI Packages') <> 0 then
Exit;
 
The function using the 0 works perfectly.   Thanks for all of your help.  The Script is working great. 
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
 Share

×
×
  • Create New...