Jump to content

[WIPz] TES5Edit


zilav

Recommended Posts

6 hours ago, Dragonblood said:

I have the script working fine I just have a couple of questions.

// IS THERE A BETTER WAY TO DO THIS?  CAN I JUST CHECK IF THE XLCM VALUE IS EVEN FILLED?  
if (GetElementEditValues(e, 'XLCM') <> 'Easy') and (GetElementEditValues(e, 'XLCM') <> 'Medium') and (GetElementEditValues(e, 'XLCM') <> 'Hard') and (GetElementEditValues(e, 'XLCM') <> 'Veryhard') then
Exit;
 
How can I create a random Int?  So I can have the settings be random.
 
For example: If XLCM is hard, then 50% of the time set it to easy or 50% of the time set it to medium.
 
if GetElementEditValues(e, 'XLCM') = 'Hard' then
	if Random(2) = 0 then
		SetElementEditValues(e, 'XLCM', 'Easy')
	else
		SetElementEditValues(e, 'XLCM', 'Medium');

And call Randomize in Initialize() otherwise you will be getting same patch values each time.

  • Like 1
Link to comment
Share on other sites

Thanks for the help and suggestions.  The script is now functioning perfectly.

I am now working on a way to run checks on the AHCR.  At this time I have no way off telling what type of actor I am changing the XLCM on.

if (GetElementEditValues(e, 'XLCM') <> 'Easy') then
Exit;
 
la := BaseRecord(e);  
 
// Is there a function that I can use here to get the Template off of la in a way I can run checks on it? 

Also  for SetElementEditValues  using the Int seems to work better.  

// set XLCM level - easy 0 , Medium 1, Hard 2, Veryhard 3
 
SetElementEditValues(r, 'XLCM', '1')

Thanks again for the help. 

 

 

Link to comment
Share on other sites

1 hour ago, Dragonblood said:

Thanks for the help and suggestions.  The script is now functioning perfectly.

I am now working on a way to run checks on the AHCR.  At this time I have no way off telling what type of actor I am changing the XLCM on.

if (GetElementEditValues(e, 'XLCM') <> 'Easy') then
Exit;
 
la := BaseRecord(e);  
 
// Is there a function that I can use here to get the Template off of la in a way I can run checks on it?
tpl := LinksTo(ElementByPath(la, 'TPLT'));

if Assigned(tpl) then { do your stuff }

Changing enumerations by raw value and enum value is the same if you provided the right value ofc.

Link to comment
Share on other sites

On 21.08.2017 at 8:35 PM, Dragonblood said:

Then I added the other lines and now neither will return.  I have no idea what the problem is.

Can you just tell what are you trying to achieve? Because that 1 to 4 loop looks weird.

  • Like 1
Link to comment
Share on other sites

1 hour ago, zilav said:

Can you just tell what are you trying to achieve? Because that 1 to 4 loop looks weird.

Yes,  Of course.

When you do this. ( If it works )

tpl := LinksTo(ElementByPath(la, 'TPLT'));

Sometimes it will be a NPC_  and sometimes it will be a LVLN.  The while is designed to keep doing it until it is an LVLN.   

The second while does the same for the LVLN.  It keeps looking through the templates until it returns NPC_  

I need to get to the Race template 

while i < 4 do
begin
if Signature(tpl) <> 'LVLN' then
tpl := LinksTo(ElementByPath(tpl, 'TPLT'))
Else
i := 4;
i := i + 1;
End;
 

I just ran a test on this.

la := BaseRecord(e);
 
tpl := LinksTo(ElementByPath(la, 'TPLT'));
 
If (Signature(tpl) = 'LVLN') or (Signature(tpl) = 'ACT_') then
Exit;

It does not stop the script it just keeps going.  Like I said I tested this early today and it worked.  Now it will not return again. 

Link to comment
Share on other sites

35 minutes ago, Dragonblood said:

Yes,  Of course.

I mean what is your final aim? Detect NPC's race or what? I have a feeling that you don't understand how templates work in Beth games because unless "Use Traits" flag is set, race is not inherited iirc.

  • Like 1
Link to comment
Share on other sites

26 minutes ago, zilav said:

I mean what is your final aim? Detect NPC's race or what? I have a feeling that you don't understand how templates work in Beth games because unless "Use Traits" flag is set, race is not inherited iirc.

That feeling would be wrong.  What I am doing is going all the way to one of the NPCs on the leveled Char list.  Then get their Race Template and check for the ActorTypeNPC keyword.  

Look.

la := BaseRecord(e);    -  This is going to be an NPC_  
 
tpl := LinksTo(ElementByPath(la, 'TPLT'));  -  ( If this works ) It will be either a NPC_ Template or a LVLN Template.  
 
I need to get one of the NPCs from the LVLN tempate.    That is what this is supposed to do.
tpl := LinksTo(ElementByPath(tpl, 'Leveled List Entries\Leveled List Entry\LVLO - Base Data\Reference'))
Then I get the Race Template. 
tpl := LinksTo(ElementByPath(tpl, 'RNAM'));
Then check it for the keyword.
if (GetElementEditValues(tpl, 'KWDA\Keyword') <> 'ActorTypeNPC') then 
  Exit;
Link to comment
Share on other sites

34 minutes ago, Dragonblood said:

I need to get one of the NPCs from the LVLN tempate.    That is what this is supposed to do.

If you know what it is supposed to do, then just do it :)

la := BaseRecord(e);    -  This is going to be an NPC_  

tpl := LinksTo(ElementByPath(la, 'TPLT'));  -  ( If this works ) It will be either a NPC_ Template or a LVLN Template.  

while Signature(tpl) = 'LVLN' do
  tpl := LinksTo(ElementByPath(tpl, 'Leveled List Entries\Leveled List Entry\LVLO\Reference'));

tpl := LinksTo(ElementByPath(tpl, 'RNAM'));

 

  • Like 1
Link to comment
Share on other sites

1 hour ago, zilav said:

If you know what it is supposed to do, then just do it

THIS DOES NOT WORK:  tpl := LinksTo(ElementByPath(la, 'TPLT'));   :cry:

Look.  This Exits every time.  

if (GetElementEditValues(e, 'XLCM') <> 'Easy') then
Exit;
 
la := BaseRecord(e);
 
tpl := LinksTo(ElementByPath(la, 'TPLT'));
 
If (Signature(tpl) <> 'LVLN') and (Signature(tpl) <> 'NPC_') then  // It does not matter if I use or/and. 
Exit;
 
Thanks for your help.  I have learned a lot about Pascal, Delphi  and the functions of this program. :imp:
Link to comment
Share on other sites

You need to learn to debug your own scripts.

Simplest way is to store any value retrieved with a function in a variable and print the values of those variables with AddMessage immediately after they're set.

You also need more error handling. For example, if ElementBySignature(la, 'TPLT') gets an unassigned element, LinksTo will fail, and tpl will be null. LinksTo might throw an exception as well. So, you need to be sure to validate any data you pass forward.

Also, keep this reference handy: https://www.creationkit.com/index.php?title=TES5Edit_Scripting_Functions

In my experience, the ElementBy* functions are sensitive to the value type, so for example, you should be using ElementBySignature(la, 'TPLT') because 'TPLT' is a signature and not a path.

  • Like 1
Link to comment
Share on other sites

2 hours ago, fireundubh said:

You need to learn to debug your own scripts.

It would be helpful if I knew how to display messages like I can in Papyrus.  That would make debugging easier.

Thank You for information regarding the ElementBy.   Zilav is the one who told me to use this tpl := LinksTo(ElementByPath(la, 'TPLT'));  

I figured he had a good idea of the best function to use.   

 

Link to comment
Share on other sites

1 hour ago, Dragonblood said:

It would be helpful if I knew how to display messages like I can in Papyrus.  That would make debugging easier.

AddMessage takes a string argument and outputs that string to the information tab/console.

See the link I posted for tables of functions and their return types.

Also, looking at the xEdit source:

  • ElementByPath will call ElementByName if the value is not a path.
  • Neither ElementByPath nor ElementByName call ElementBySignature, so I don't see how they'd be able to get elements by signatures.

 

  • Like 1
Link to comment
Share on other sites

OK,  Thanks to Fireundubh for the ElementBySignature information and of course all of the help form Zilav. 

1.  I am getting the LVLN Template

2.  I am getting the ACT_  Template

3.  I am getting the RACE Template

Everything is working up to this point, but this:   It is Exiting everyone.  Like I said I have the Race Template.  ( Tested )

What am I doing  wrong?  I can not see it.

if (GetElementEditValues(tpl, 'KWDA\Keyword') <> 'ActorTypeNPC')  then
Exit;

Thanks Again for all the help. 

Link to comment
Share on other sites

1 hour ago, Dragonblood said:

OK, I am starting to get somewhere. Thanks to fireundubh for the ElementBySignature information and of course all of the help form Zilav.

My original code has if Assigned(tpl) check but you omitted it, I guessed you are simply copy/pasting code parts and your full script has it.

Path is an universal method to access elements and works fine with both names and signatures. When I write my own scripts I use all of them depending on context: ByName, BySignature and ByPath. However when writing code for someone else I almost always stick to ByPath just in case that person would like to copy/paste it and replace with his own value like you've done when getting a reference from LVLN (ElementBySignature wouldn't have worked).

I still don't understand the purpose of you code to get the "root" template NPC record because getting a first entry from LVLN already makes it inconsistent since the game can spawn from any entry in a leveled list. But anyway, here is a fully working example :

unit userscript;

function Process(e: IInterface): Integer;
var
  npc, tpl: IInterface;
begin
  if Signature(e) <> 'NPC_' then
    Exit;

  npc := e;
  tpl := LinksTo(ElementByPath(npc, 'TPLT'));
  
  while Assigned(tpl) do begin
    npc := tpl;
    if Signature(tpl) = 'LVLN' then
      tpl := LinksTo(ElementByPath(tpl, 'Leveled List Entries\Leveled List Entry\LVLO\Reference'))
    else
      tpl := LinksTo(ElementByPath(tpl, 'TPLT'));
  end;

  AddMessage(Name(e) + ' is templated from root ' + Name(npc));
end;
	
end.

 

  • Like 1
Link to comment
Share on other sites

5 hours ago, zilav said:

My original code has if Assigned(tpl) check but you omitted it

It is in my script and very important.  It will not work without it.

5 hours ago, zilav said:

I still don't understand the purpose of you code to get the "root" template NPC record because getting a first entry from LVLN already makes it inconsistent since the game can spawn from any entry in a leveled list.

Yes,  But they will always be 'ActortypeNPC', or 'ActortypeUndead' for Draugr.   Besides that there is no other way to do it. 

5 hours ago, zilav said:

here is a fully working example :

Nice code.  My question would be.  What stops the While?  

I am getting the LVLN and the NPC_ and  RACE templates. 

This is the only place the script is failing at this time.   Is it done properly?  I have the Race Template.

if (GetElementEditValues(tpl, 'KWDA\Keyword') <> 'ActorTypeNPC')  then
Exit;

Thanks for the help.

Link to comment
Share on other sites

7 minutes ago, Dragonblood said:

This is the only place the script is failing at this time.  

Give me any vanilla record FormID where scripts fails to get the root template.

Regarding keywords, you obviouly need to loop over them

function HasKeyword(r: IInterface; aKeyword: string): boolean;
var
  keywords: IInterface;
  i: integer;
begin
  keywords := ElementBySignature(r, 'KWDA');
  for i := 0 to Pred(ElementCount(keywords)) do
    if EditorID(LinksTo(ElementByIndex(keywords, i))) = aKeyword then begin
      Result := True;
      Exit;
    end;
end;

 

  • Like 1
Link to comment
Share on other sites

1 hour ago, zilav said:

Give me any vanilla record FormID where scripts fails to get the root template.

I am not sure what you are talking about.  I was referring to my script.   

I could not get this to work via ByPath.  I had to use BySignature.

tpl := LinksTo(ElementBySignature(tpl, 'TPLT'));

This works by path. 

tpl := LinksTo(ElementByPath(tpl, 'Leveled List Entries\Leveled List Entry\LVLO\Reference'));

This has been a new experience for me and I have a lot to learn about this program and Delphi Code. 

The script is now functioning very well.   Your Keyword Function was the solution. 

Thanks for helping me.  This has been fun and a little frustrating.  Without  help it would have been only frustrating.   It has been like learning a new language.  

Would you like to see this Masterpiece?  It is quite long. 

Link to comment
Share on other sites

12 hours ago, Dragonblood said:

I could not get this to work via ByPath.  I had to use BySignature.

Yeah, zilav has told me before that ElementByPath works for signatures, but that hasn't been my experience, so I wrote a helper function so I didn't need to think about it.

// --------------------------------------------------------------------
// Returns any element from a string
// --------------------------------------------------------------------
function GetElement(const x: IInterface; const s: String): IInterface;
begin
	if Length(s) > 0 then begin
		if Pos('[', s) > 0 then
			Result := ElementByIP(x, s) // ElementByIP requires matortheeternal's mteFunctions library
		else if Pos('\', s) > 0 then
			Result := ElementByPath(x, s)
		else if s = Uppercase(s) then
			Result := ElementBySignature(x, s)
		else
			Result := ElementByName(x, s);
	end;
end;

You can use my dubhFunctions library (which requires mteFunctions), or add this to your own projects without the ElementByIP condition (assuming you're not using mteFunctions).

I also implemented a similar helper function in my xEdit Pro fork (xEdit is 200-300 commits ahead of my fork now) that supports all core ElementBy* functions:

procedure IwbContainer_GetElement(var Value: Variant; Args: TJvInterpreterArgs);
var
  Container: IwbContainerElementRef;
  Signature: TwbSignature;
  vElement: Variant;
  sElement: String;
  basicType: Integer;
begin
  if Supports(IInterface(Args.Values[0]), IwbContainerElementRef, Container) then begin
    vElement := Variant(Args.Values[1]);
    basicType := VarType(vElement) and VarTypeMask;

    if (basicType = varString) or (basicType = varUString) then begin
      sElement := String(Args.Values[1]);
      // ElementByPath
      if Pos('\', sElement) > 0 then
        Value := Container.ElementByPath[sElement]
      // ElementBySignature
      else if sElement = Uppercase(sElement) then begin
        Signature := StrToSignature(sElement);
        Value := Container.ElementBySignature[Signature];
      // ElementByName
      end else
        Value := Container.ElementByName[sElement];
    // ElementByIndex
    end else
      Value := Container.Elements[Integer(Args.Values[1])];
  end;
end;

VarType and VarTypeMask aren't supported in xEdit scripts by default, so unfortunately, you'd have to add this procedure to wbScriptAdapter.pas and compile xEdit yourself to use this.

However, when I was modding and writing xEdit scripts, I actually began to prefer using the ElementBy* functions explicitly and directly for the clarity they provided.

  • Thanks 1
Link to comment
Share on other sites

I  want to thank the people who have helped me here and apologize for anything I may have gotten wrong.  

The Script is now able to get Every Leveled Actor ( Falmer, Undead, NPC ) and change the Level of the Reference Template in Dawnguard in 7 seconds.   It started at 20.  Great program.

I made this post because I am unable to get  ( on Ex: Exception ) to work in a function without End. and that will stop the entire script.  

However I do not think it will ever work that way so I will have to do without it.  

I do have one quick question about this script.  How would the while be stopped?   Is the 

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

under begin being checked every time the while finishes a loop?   

unit userscript;

function Process(e: IInterface): Integer;
var
  npc, tpl: IInterface;
begin
  if Signature(e) <> 'NPC_' then
    Exit;

  npc := e;
  tpl := LinksTo(ElementByPath(npc, 'TPLT'));
  
  while Assigned(tpl) do begin
    npc := tpl;
    if Signature(tpl) = 'LVLN' then
      tpl := LinksTo(ElementByPath(tpl, 'Leveled List Entries\Leveled List Entry\LVLO\Reference'))
    else
      tpl := LinksTo(ElementByPath(tpl, 'TPLT'));
  end;

  AddMessage(Name(e) + ' is templated from root ' + Name(npc));
end;
	
end.

 

Link to comment
Share on other sites

10 hours ago, Dragonblood said:

I do have one quick question about this script.  How would the while be stopped?   Is the 

 

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

under begin being checked every time the while finishes a loop?  

That check ensures that script processes only NPC_ records.

A 'while' loop stops when template can not be get anymore from the current record be it LVLN or NPC_

  • Thanks 1
Link to comment
Share on other sites

6 hours ago, zilav said:

A 'while' loop stops when template can not be get anymore from the current record be it LVLN or NPC_

Got it.  Thanks again.  

Link to comment
Share on other sites

I need to detect and delete test cells, and the actors in them, from the results of my script that identifies Leveled actors and changes their level.  

What would be the best way to do that? 

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...