zilav Posted August 21, 2017 Author Share Posted August 21, 2017 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. Dragonblood 1 Link to comment Share on other sites More sharing options...
Dragonblood Posted August 21, 2017 Share Posted August 21, 2017 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 More sharing options...
zilav Posted August 21, 2017 Author Share Posted August 21, 2017 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 More sharing options...
zilav Posted August 21, 2017 Author Share Posted August 21, 2017 Uploaded new build which includes FO4LODGen Either rename to FO4LODGen.exe or use Right Click -> Other -> Generate LOD menu in FO4Edit You will also need to install FO4LODGen Resources https://mega.nz/#!NQYg3YoR!IJI0-K2Id94LnScyb2RAW5hcBl0LEVSeXdBb71HlKpQ Beermotor 1 Link to comment Share on other sites More sharing options...
Dragonblood Posted August 21, 2017 Share Posted August 21, 2017 This post was just taking up space so I deleted it. Link to comment Share on other sites More sharing options...
zilav Posted August 22, 2017 Author Share Posted August 22, 2017 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. Dragonblood 1 Link to comment Share on other sites More sharing options...
Dragonblood Posted August 22, 2017 Share Posted August 22, 2017 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 More sharing options...
zilav Posted August 22, 2017 Author Share Posted August 22, 2017 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. Dragonblood 1 Link to comment Share on other sites More sharing options...
Dragonblood Posted August 22, 2017 Share Posted August 22, 2017 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 More sharing options...
zilav Posted August 22, 2017 Author Share Posted August 22, 2017 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')); Dragonblood 1 Link to comment Share on other sites More sharing options...
Dragonblood Posted August 22, 2017 Share Posted August 22, 2017 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')); 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. Link to comment Share on other sites More sharing options...
fireundubh Posted August 23, 2017 Share Posted August 23, 2017 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. Dragonblood 1 Link to comment Share on other sites More sharing options...
Dragonblood Posted August 23, 2017 Share Posted August 23, 2017 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 More sharing options...
fireundubh Posted August 23, 2017 Share Posted August 23, 2017 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. Dragonblood 1 Link to comment Share on other sites More sharing options...
Dragonblood Posted August 23, 2017 Share Posted August 23, 2017 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 More sharing options...
zilav Posted August 23, 2017 Author Share Posted August 23, 2017 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. Dragonblood 1 Link to comment Share on other sites More sharing options...
Dragonblood Posted August 23, 2017 Share Posted August 23, 2017 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 More sharing options...
zilav Posted August 23, 2017 Author Share Posted August 23, 2017 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; Dragonblood 1 Link to comment Share on other sites More sharing options...
Dragonblood Posted August 23, 2017 Share Posted August 23, 2017 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 More sharing options...
fireundubh Posted August 24, 2017 Share Posted August 24, 2017 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. Dragonblood 1 Link to comment Share on other sites More sharing options...
Dragonblood Posted August 24, 2017 Share Posted August 24, 2017 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 More sharing options...
zilav Posted August 25, 2017 Author Share Posted August 25, 2017 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_ Dragonblood 1 Link to comment Share on other sites More sharing options...
Dragonblood Posted August 25, 2017 Share Posted August 25, 2017 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 More sharing options...
Dragonblood Posted August 28, 2017 Share Posted August 28, 2017 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 More sharing options...
zilav Posted August 28, 2017 Author Share Posted August 28, 2017 Define what is a "test", it is too vague. Cells containing "test" word in Editor ID or what? Dragonblood 1 Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now