Jump to content

[WIPz] TES5Edit


zilav

Recommended Posts

1 hour ago, MatthiosArcanus said:

Alright, still testing, but I figured I would stop in and see if I could get any additional pointers.
This time, though, I have code!

So all I'm doing right now is essentially getting my head around how this all works together.
At the moment, I'm trying to verify that I'm getting the correct values from the JSON file.

Unfortunately I'm not, as I get an index out of bounds error when I try to run the above code when looking at index 0.
I have a feeling I'm not populating the object correctly when I write it to the file, as it doesn't seem to mesh with a FO4 blueprint I found.

I'm going to keep poking at this, though, maybe try something a little less nested.

You don't need to create all those json children objects and arrays yourself, they are automatically created as soon as you access them.

For example

sub_Obj := main_Obj.O['RECORDS'];

creates a new json object named 'RECORDS' inside main_obj object and returns you it's instance, so the previous line

sub_Obj := TJsonObject.Create;

is not needed at all. You also don't need to free children yourself, they all will be freed automatically when you do main_Obj.Free since it is a parent to them.

I simplified your script

https://pastebin.com/raw/CtAWAC9D

 

Link to comment
Share on other sites

Huh, very interesting!

Let me see if I've got this straight.

In the TestWrite function, we've got this line.
main_Array := main_Obj.O['RECORDS'].A['SILVER'].AddObject.A['BATTLEAXE'];
If I'm understanding this correctly, the first thing this line does is create an object called 'RECORDS,' then proceeds to create an array within RECORDS called 'SILVER.' It finishes off by creating another array, 'BATTLEAXE,' within SILVER. The only part that's unclear to me is the relationship between main_Array and main_Obj. I would say that main_Array is storing what was just created, but I'm not so sure as we're writing main_Obj to file, not main_Array.

As far as what's in JsonTest, this essentially seems like a sort of reverse of the above.
main_Obj is loading, and then storing, the data from the JSON file. a01 then stores what is in the array of BATTLEAXE, which is stored in SILVER, which is a child of RECORDS.
After that, it looks like a simple index accessor to get the string version of what's stored in 0.

Do let me know if any of that is correct.
I'm not a fan of just taking solutions and not understanding them.
You can't really apply them to other areas if you don't understand the initial application, is the way I see it.

Thanks so much for the help, it's greatly appreciated. =-)

Link to comment
Share on other sites

main_Array is an instance of json array inside the json object structure of main_Obj. When you save main_Obj, you save everything inside of it (to which it is a parent element). And main_Array is just a shortcut to access that array quickly instead of typing main_Obj.O['RECORDS'].A['SILVER'].AddObject.A['BATTLEAXE']  again. You can actually call main_Array.SaveToFile() too to save only this element of json.

Link to comment
Share on other sites

Ah, I see. =-)

So for instance, these lines:
main_Array := main_Obj.O['WEAPONS'].A['SILVER'].AddObject.A['BATTLEAXE'];
main_Array.Add(18);

Are basically this?
main_Obj.O['WEAPONS'].A['SILVER'].AddObject.A['BATTLEAXE'].Add(18);
 

Link to comment
Share on other sites

Yes. Instead of storing TJsonArray instance that main_Obj.O['WEAPONS'].A['SILVER'].AddObject.A['BATTLEAXE'] returns, you just call Add() on it.

The same for main_Obj.O['WEAPONS'] which returns newly created TJsonObject on which you call A['SILVER'], which returns created TJsonArray on which you call AddObject(), etc.

Link to comment
Share on other sites

On 8/16/2017 at 1:28 PM, zilav said:

I added comments to make it easier to understand. Apply to plugins, worldspaces or cells you want to patch.

https://pastebin.com/raw/VusbthMY

Well I figure one script deserves another.  I am sure the Bug Guys have already fixed this MineOreFurnitureScript so it stops locking up if you spam it.  This is my version.   Thanks Again for the help.  If you have any guides or tutorials I would also like to have a look at them.  

Scriptname MineOreFurnitureScript extends ObjectReference  Conditional
{script for furniture which the player can use to get resources}

objectReference property lastActivateRef auto Hidden
;objectReference property objSelf auto hidden
idle property PickaxeExit auto
faction property CurrentFollowerFaction auto
globalvariable property MiningSkillIncrement auto
  
;===================================================================
;;EVENTS
;===================================================================
Event OnLoad()
	BlockActivation(true)
endEvent

Event OnCellDetach()
UnRegisterForAnimationEvent(Game.GetPlayer(), "AddToInventory")
endEvent

Event OnAnimationEvent(ObjectReference akSource, string asEventName)
	If asEventName == "AddToInventory"
		lastActivateRef.activate(Self As ObjectReference)
	Endif
endEvent
	
Event OnActivate(ObjectReference akActionRef)
If akActionRef == Game.GetPlayer() 
Else
Return
EndIf
If isFurnitureInUse() 
	If (akActionRef As Actor).GetSitState()
	Else 
		(Game.GetFormFromFile(0x000DED93, "Skyrim.esm") As Message).Show()  ;TG08AGateMessage
	EndIf
	Return
EndIf
RegisterForAnimationEvent(akActionRef, "AddToInventory")
Activate(akActionRef, true)
GotoState("busy")	
EndEvent

;===================================================================
;;STATES
;===================================================================
  
STATE busy
	Event onActivate(objectReference akActionRef) 
		If IsFurnitureInUse()
			Reseting(Game.GetPlayer())
		EndIf	
	EndEvent
endState

state Depleted
	event onActivate(objectReference akActionRef)
	endEvent
endState

;===================================================================
;;FUNCTIONS
;===================================================================

function goToDepletedState()
	UnRegisterForAnimationEvent(Game.GetPlayer(), "AddToInventory")
	GoToState("Depleted")
endFunction
  
Function Reseting(ObjectReference p)
    GoToState("Depleted")
	(p As Actor).PlayIdle(PickaxeExit)
	UnRegisterForAnimationEvent(p, "AddToInventory")
	GoToState("")
EndFunction

 

Link to comment
Share on other sites

3 hours ago, zilav said:

Yes. Instead of storing TJsonArray instance that main_Obj.O['WEAPONS'].A['SILVER'].AddObject.A['BATTLEAXE'] returns, you just call Add() on it.

The same for main_Obj.O['WEAPONS'] which returns newly created TJsonObject on which you call A['SILVER'], which returns created TJsonArray on which you call AddObject(), etc.

Awesome, that's good to know. =-)

I may come back with more questions, but for now I want to poke at things a bit, lol.

Link to comment
Share on other sites

Alright, one more question.

Let's see if I can explain this correctly.

Silver is one of many types of weapons I want to store. For simplicity, let's say we have silver and druagr.
The structure of this is something like this.

RECORDS
> SILVER
>>>> BATTLEAXE
>>>>>>> <Number>
>>>>>>> <Number>
>>>> BOW
>>>>>>> <Number>
>>>>>>> <Number>
>DRAUGR
>>>> BATTLEAXE
>>>>>>> <Number>
>>>>>>> <Number>
>>>> BOW
>>>>>>> <Number>
>>>>>>> <Number>

What I'll be needing to do is iterate through the records that fall under the silver and draugr categories.
In other words, I need a way to get to silver or draugr, or any other type of weapon that I need to add, other than explicitly saying what I want, and from there, it looks like it will be simple enough to get their weapon types/stats.
Just one missing piece, it seems.

Thanks for any suggestions!
 

Link to comment
Share on other sites

9 hours ago, MatthiosArcanus said:

Thanks for any suggestions!

I need to understand the purpose of this json first.

Do you plan to store anything else in this json apart from the list of "RECORDS"? If no, then you don't need the top "RECORDS" object.

Also what do those numbers mean? If they are some weapon properties, then better to store them as object with named fields like "Weight", "Value", etc.

Link to comment
Share on other sites

Certainly. =-)

Originally, I had intended the records to be an identifier between two types of records, weapons and armors.
Overall though, I think it can definitely be discarded in favor of two separate files.

The numbers are indeed properties, specifically damage, critical damage, value, and weight.

Let me see if I can provide more information on all this.
Basically what I'm wanting to achieve is a large list of reference info, something I can check unknown data against.
Once written, it's going to be static, nothing will change about it unless I mess something up in the write to file.

If you would like to know overall purpose, or the way this fits into the bigger picture, I can hopefully provide that as well.
In essence, I'm making a balancing script, something you run before all other patching processes.
What it does is look at all mod-added weapons and armors, compares them to this reference info, and changes their stats, damage, etc. to match the reference if a change is needed.

If any other info is needed, however, do let me know!


As an aside, I am curious about your thoughts concerning something due to having some doubt: do you think JSON is the way to go for this?

Link to comment
Share on other sites

1 hour ago, MatthiosArcanus said:

As an aside, I am curious about your thoughts concerning something due to having some doubt: do you think JSON is the way to go for this?

JSON is simply a data storage method, it all depends on how data is going to be created, edited and read.

JSON and XML are formats to exchange data between machines, they are not the best choice when a human is supposed to edit because of strict format rules and humans being prone to making errors.

If you are going to create/edit those files yourself, then it comes down to how you are going to do that. If you want a spreadsheet like tabled representation, then I suggest CSV.

If Notepad is your choice, then probably INI format or even a plain text file.

So, what is your choice? I can create you an example for reading/writing any of them: JSON, CSV, INI, plain text.

Link to comment
Share on other sites

Hm, good points. =-)
I appreciate the feedback!

I would say that, considering how much trouble I'm having getting my head around it, JSON is a no-go.
CSV may be good, but then again, never worked with that, whereas I have at least worked with JSON before (although it has been a long time,) so I would consider that off the table.

So it comes down to INI or text.
From what I've seen and understood when it comes to Skyrim's INI files is that you can define what type a value is.
Considering it would be best if I have access to strings and floating point numbers, INI seems like the best route.
I think, then, that a text file can be ruled out, as that only seems best for string data.

Yeah, I would definitely say INI is the way to go in this situation.
 

Link to comment
Share on other sites

I love how simple that is, I can actually comprehend what's going on, lol.
Thanks so much!

There is one thing, though, that I don't understand.
In Initialize, what is the purpose of setting Result to 1?

Link to comment
Share on other sites

Wanted to come back after fully implementing this and let you know of the results.

I have no words, honestly. It works beautifully.
It does in around 10 lines of code what it took 15K lines of code to do previously (granted, I misunderstood the documentation and didn't think arrays were a thing when I wrote that... The joys of being new, I suppose.)

Anyways, it's just awesome how well it works.
I should stop saying "thanks," so instead I'll say "you're awesome," lol.

Take care!

Link to comment
Share on other sites

On 8/16/2017 at 1:28 PM, zilav said:

I added comments to make it easier to understand. Apply to plugins, worldspaces or cells you want to patch.

https://pastebin.com/raw/VusbthMY

Zilav, 

I finely got a chance to test out the script, there are a couple of small issues. 

1. It will not allow me to choose which esp to save the changes too.  It forces me to create a new one .

2. When it is run it displays this error.  

Failed to copy:  \ [03] HearthFires.esm \ [32] GRUP Top "WRLD" \ [1] GRUP World Children of Tamriel "Skyrim" [WRLD:0000003C] \ [2] GRUP Exterior Cell Block 0, 0 \ [1] GRUP Exterior Cell Sub-Block 1, 0 \ [9] GRUP Cell Children of BYOHHouse3Exterior [CELL:000090C4] (in Tamriel "Skyrim" [WRLD:0000003C] at 7,8) \ [0] GRUP Cell Temporary Children of BYOHHouse3Exterior [CELL:000090C4] (in Tamriel "Skyrim" [WRLD:0000003C] at 7,8) \ [14] [REFR:030167A7] (places BYOHMineStone01 "Stone Quarry" [ACTI:0300306B] in GRUP Cell Temporary Children of BYOHHouse3Exterior [CELL:000090C4] (in Tamriel "Skyrim" [WRLD:0000003C] at 7,8))
    reason: Load order FormID [0000003C] can not be mapped to file FormID for file "Test.esp"


Failed to copy:  \ [03] HearthFires.esm \ [32] GRUP Top "WRLD" \ [1] GRUP World Children of Tamriel "Skyrim" [WRLD:0000003C] \ [2] GRUP Exterior Cell Block 0, 0 \ [0] GRUP Exterior Cell Sub-Block 0, 0 \ [5] GRUP Cell Children of BleakwindBasinExterior01 [CELL:000099A2] (in Tamriel "Skyrim" [WRLD:0000003C] at 0,0) \ [0] GRUP Cell Temporary Children of BleakwindBasinExterior01 [CELL:000099A2] (in Tamriel "Skyrim" [WRLD:0000003C] at 0,0) \ [0] [REFR:03000AB6] (places BYOHMineClay01Dirt02 "Clay Deposit" [ACTI:0300310B] in GRUP Cell Temporary Children of BleakwindBasinExterior01 [CELL:000099A2] (in Tamriel "Skyrim" [WRLD:0000003C] at 0,0))
    reason: Load order FormID [0000003C] can not be mapped to file FormID for file "Test.esp"


Failed to copy:  \ [03] HearthFires.esm \ [32] GRUP Top "WRLD" \ [1] GRUP World Children of Tamriel "Skyrim" [WRLD:0000003C] \ [2] GRUP Exterior Cell Block 0, 0 \ [0] GRUP Exterior Cell Sub-Block 0, 0 \ [3] GRUP Cell Children of WhitewatchTowerExterior01 [CELL:0000915F] (in Tamriel "Skyrim" [WRLD:0000003C] at 7,3) \ [0] GRUP Cell Temporary Children of WhitewatchTowerExterior01 [CELL:0000915F] (in Tamriel "Skyrim" [WRLD:0000003C] at 7,3) \ [0] [REFR:03000B1E] (places BYOHMineStone01 "Stone Quarry" [ACTI:0300306B] in GRUP Cell Temporary Children of WhitewatchTowerExterior01 [CELL:0000915F] (in Tamriel "Skyrim" [WRLD:0000003C] at 7,3))
    reason: Load order FormID [0000003C] can not be mapped to file FormID for file "Test.esp"

As you can see it does seem to be finding the ObjectReferences.  

Again , Thank You for you help.  

 

Link to comment
Share on other sites

5 hours ago, Dragonblood said:

1. It will not allow me to choose which esp to save the changes too.  It forces me to create a new one .

2. When it is run it displays this error.

Ah, the rare occasion when REFR doesn't require the same masters as it's parent cell. Try this one, it will also ask to create a new plugin or append to the last loaded one.

https://pastebin.com/raw/Rwr7Fsfr

  • Like 1
Link to comment
Share on other sites

1 hour ago, zilav said:

Try this one

Ya,  That works.  It completed in about half a second.  Thanks, This is by far the best way for me to learn.  I am going to research this Script,  Pascal and the XEdit functions page I found on the CKWiki and try one.  

It is going to check the level of the Leveled Actors ObjectReference Templates and adjust their level based on current level.  

For example:  If they are set to Veryhard set to Hard.  

Thanks Again.

 

Link to comment
Share on other sites

Hi zilav,

Does the latest xEdit version posted here changed anything regarding the right-click menu command "Change FormID"?

It behaves totally different from version 3.2 (the one posted at Nexus).

Using it for SSE...

 

Thanks!

Link to comment
Share on other sites

20 minutes ago, Astakos said:

Hi zilav,

Does the latest xEdit version posted here changed anything regarding the right-click menu command "Change FormID"?

It behaves totally different from version 3.2 (the one posted at Nexus).

Using it for SSE...

 

Thanks!

Download again, there was a wrong key state check.

Link to comment
Share on other sites

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.
 
Link to comment
Share on other sites

6 hours ago, Dragonblood said:

How can I create a random Int?  So I can have the settings be random.

http://www.delphibasics.co.uk/RTL.asp?Name=Random

http://www.delphibasics.co.uk/RTL.asp?Name=RandomRange

http://www.delphibasics.co.uk/RTL.asp?Name=Randomize

I don't remember if the main branch of xEdit has these functions implemented. Just try them.

  • Like 1
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...